Compare commits

..

2 Commits

7 changed files with 123780 additions and 140 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,36 @@
" return temp"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plot_histogram(ax, data, bin_count, title, signal_name):\n",
" n, bins, patches = ax.hist(data, bin_count, density=1, color='navy')\n",
" mu = np.mean(data)\n",
" sigma = np.std(data)\n",
" y = ((1 / (np.sqrt(2 * np.pi) * sigma)) * np.exp(-0.5 * (1 / sigma * (bins - mu))**2))\n",
" ax.plot(bins, y, color='darkorange')\n",
" ax.set_title(title)\n",
" ax.set_ylabel(signal_name + ' probability (normalized)')\n",
" ax.set_xlabel(signal_name)\n",
" # Plot sigma and mu lines\n",
" ax.axvline(x=mu-sigma, ls='--', color='magenta')\n",
" ax.axvline(x=mu+sigma, ls='--', color='magenta')\n",
" ax.axvline(x=mu, ls='--', color='lawngreen')\n",
"\n",
" #Plot textbox\n",
" textstr = '\\n'.join((\n",
" r'$\\mu=%.2f$' % (mu, ),\n",
" r'$\\sigma=%.3f$' % (sigma, ),\n",
" r'$N_{Sa} =%d$' % (len(data), )))\n",
" props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)\n",
" ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14,\n",
" verticalalignment='top', bbox=props)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -117,33 +147,14 @@
"metadata": {},
"outputs": [],
"source": [
"fig, axes = plt.subplots(nrows=3, ncols=3, figsize=(28,20))\n",
"plot_data = [(one_k_sampling_trafo, '1 kOhm Sampling Transformer powered', 0), (two_k_sampling_trafo, '2 kOhm Sampling Transformer powered' , 0), (constant_sampling, '1 kOhm Sampling', 100)]\n",
"signal_list = [('adc_results.pa2_raw', 20), ('ext_lf_corr', 20), ('temp_calculated', 20)]\n",
"signal_list = [('ext_lf_corr', 20), ('temp_calculated', 20)]\n",
"\n",
"fig, axes = plt.subplots(nrows=len(plot_data), ncols=len(signal_list), figsize=(28,20))\n",
"\n",
"for (data_df, title, start_idx), ax_rows in zip(plot_data, axes):\n",
" for ax,sig in zip(ax_rows, signal_list):\n",
" n, bins, patches = ax.hist(data_df[sig[0]][start_idx:], sig[1], density=1, color='navy')\n",
" mu = np.mean(data_df[sig[0]][start_idx:])\n",
" sigma = np.std(data_df[sig[0]][start_idx:])\n",
" y = ((1 / (np.sqrt(2 * np.pi) * sigma)) * np.exp(-0.5 * (1 / sigma * (bins - mu))**2))\n",
" ax.plot(bins, y, color='darkorange')\n",
" ax.set_title(title)\n",
" ax.set_ylabel(sig[0] + ' probability (normalized)')\n",
" ax.set_xlabel(sig[0])\n",
" # Plot sigma and mu lines\n",
" ax.axvline(x=mu-sigma, ls='--', color='magenta')\n",
" ax.axvline(x=mu+sigma, ls='--', color='magenta')\n",
" ax.axvline(x=mu, ls='--', color='lawngreen')\n",
" \n",
" #Plot textbox\n",
" textstr = '\\n'.join((\n",
" r'$\\mu=%.2f$' % (mu, ),\n",
" r'$\\sigma=%.2f$' % (sigma, ),\n",
" r'$N_{Sa} =%d$' % (len(data_df[sig[0]][start_idx:], ))))\n",
" props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)\n",
" ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14,\n",
" verticalalignment='top', bbox=props)\n",
" plot_histogram(ax, data_df[sig[0]][start_idx:], sig[1], title,sig[0])\n",
"\n",
" \n",
"plt.tight_layout()\n",
@ -310,6 +321,66 @@
"ax.set_title('Cooldown without airflow | Convection has to be taken into account') \n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"# Target Implementation Sampling of 1k Resistor"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Histograms"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Read in data\n",
"kilo_ohm_sampling1 = pd.read_csv(r'1000OhmSampling1ms.csv')\n",
"kilo_ohm_sampling2 = pd.read_csv(r'1000OhmSampling1ms_later.csv')\n",
"kilo_ohm_sampling2_fast = pd.read_csv(r'1000OhmSamplingTimerTriggered800ARR.csv')\n",
"plot_data_tuples = [(kilo_ohm_sampling1, 'Day 1 Sampling'), (kilo_ohm_sampling2, 'Day 2 sampling'), (kilo_ohm_sampling2_fast,'Day 2 sampling faster (ARR=700)')]\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"# def plot_histogram(ax, data, bin_count, title, signal_name):\n",
"# def calculate_temp_for_df(data_frame, resistance_col_name='ext_lf_corr', temp_col_name='temp_calculated'):\n",
"\n",
"fig, axes = plt.subplots(nrows=len(plot_data_tuples), ncols=2, sharex='col', figsize=(28, 16))\n",
"\n",
"for df, title in plot_data_tuples:\n",
" calculate_temp_for_df(df, resistance_col_name='pt1000_res_raw_lf')\n",
"\n",
"for (df,title),ax in zip(plot_data_tuples, axes):\n",
" plot_histogram(ax[0], df['pt1000_res_raw_lf'], 20, title, 'PT1000 Resistance')\n",
" plot_histogram(ax[1], df['temp_calculated'], 20, title, 'Calculated Temperature in °C')\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"calc_temp(1097)"
]
}
],
"metadata": {

View File

@ -1,30 +1,98 @@
#include <adc-meas.h>
#include <stm32f4xx.h>
#include <core_cm4.h>
#include <stm32-gpio-macros.h>
#include <stdlib.h>
static float pt1000_offset;
static float pt1000_sens_dev;
static bool calibration_active;
static float filter_alpha;
static float pt1000_adc_raw_lf;
static bool streaming_active;
static volatile float pt1000_res_raw_lf;
static volatile bool streaming_active;
static volatile bool filter_ready;
static volatile enum adc_p1000_error pt1000_error;
static volatile enum adc_pt1000_error pt1000_error;
static volatile uint8_t *dma_flag_ptr = NULL;
static uint32_t filter_startup_cnt;
#define ADC_TO_RES(adc) ((float)(adc) / 4096.0f * 2500.0f)
static inline void adc_pt1000_stop_sample_frequency_timer()
{
TIM2->CR1 &= ~TIM_CR1_CEN;
RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN;
}
static inline void adc_pt1000_setup_sample_frequency_timer()
{
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
/* Divide 42 MHz peripheral clock by 42 */
TIM2->PSC = (42UL-1UL);
/* Reload value */
TIM2->ARR = ADC_PT1000_SAMPLE_CNT_DELAY;
/* Trigger output at update event */
TIM2->CR2 = TIM_CR2_MMS_1;
/* Start Timer in downcounting mode with autoreload */
TIM2->CR1 = TIM_CR1_DIR | TIM_CR1_CEN;
}
static inline void adc_pt1000_disable_adc()
{
ADC1->CR2 &= ~ADC_CR2_ADON;
DMA2_Stream0->CR = 0;
RCC->APB2ENR &= ~RCC_APB2ENR_ADC1EN;
}
void adc_pt1000_setup_meas()
{
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
RCC->AHB1ENR |= ADC_PT1000_PORT_RCC_MASK;
ADC_PT1000_PORT->MODER |= ANALOG(ADC_PT1000_PIN);
/* Set S&H time for PT1000 ADC channel */
#if ADC_PT1000_CHANNEL < 10
ADC1->SMPR2 |= (7U << (3*ADC_PT1000_CHANNEL));
#else
ADC1->SMPR1 |= (7U << (3*(ADC_PT1000_CHANNEL-10)));
#endif
ADC->CCR |= (0x2<<16);
/* Set watchdog limits */
ADC1->HTR = ADC_PT1000_UPPER_WATCHDOG;
ADC1->LTR = ADC_PT1000_LOWER_WATCHDOG;
/* Set length of sequence to 1 */
ADC1->SQR1 = (0UL<<20);
/* Set channel as 1st element in sequence */
ADC1->SQR3 = (ADC_PT1000_CHANNEL<<0);
ADC1->CR1 = ADC_CR1_OVRIE | ADC_CR1_AWDEN | ADC_CR1_EOCIE;
ADC1->CR2 = ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTSEL_1 | ADC_CR2_ADON;
adc_pt1000_set_moving_average_filter_param(ADC_PT1000_FILTER_WEIGHT);
adc_pt1000_set_resistance_calibration(0, 0, false);
streaming_active = false;
pt1000_res_raw_lf = 0.0f;
NVIC_EnableIRQ(ADC_IRQn);
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
adc_pt1000_setup_sample_frequency_timer();
}
void adc_pt1000_set_moving_average_filter_param(float alpha)
{
filter_alpha = alpha;
filter_ready = false;
filter_startup_cnt = ADC_FILTER_STARTUP_CYCLES;
}
void adc_pt1000_set_resistance_calibration(float offset, float sensitivity_deviation, bool active)
@ -44,6 +112,15 @@ void adc_pt1000_get_resistance_calibration(float *offset, float *sensitivity_dev
*active = calibration_active;
}
static inline float adc_pt1000_apply_calibration(float raw_resistance)
{
if (calibration_active)
return pt1000_res_raw_lf * (1.0f + pt1000_sens_dev) + pt1000_offset;
else
return raw_resistance;
}
int adc_pt1000_get_current_resistance(float *resistance)
{
int ret_val = 0;
@ -51,24 +128,22 @@ int adc_pt1000_get_current_resistance(float *resistance)
if (!resistance)
return -1001;
if (calibration_active)
*resistance = ADC_TO_RES(pt1000_adc_raw_lf) * (1 + pt1000_sens_dev) + pt1000_offset;
else
*resistance = ADC_TO_RES(pt1000_adc_raw_lf);
*resistance = adc_pt1000_apply_calibration(pt1000_res_raw_lf);
if (adc_pt1000_check_error()) {
ret_val = -100;
goto return_value;
}
if (!filter_ready) {
ret_val = 2;
goto return_value;
}
if (streaming_active) {
ret_val = 1;
goto return_value;
}
if (!filter_ready)
{
ret_val = 2;
}
return_value:
return ret_val;
@ -76,7 +151,44 @@ return_value:
int adc_pt1000_stream_raw_value_to_memory(uint16_t *adc_array, uint32_t length, volatile uint8_t *flag_to_set)
{
static volatile uint8_t alt_flag;
int ret_val = 0;
if (!(ADC1->CR2 & ADC_CR2_ADON))
return -1;
if (!adc_array || !length)
return -1000;
if (flag_to_set)
dma_flag_ptr = flag_to_set;
else
dma_flag_ptr = &alt_flag;
*dma_flag_ptr = 0;
streaming_active = true;
ADC1->CR2 &= ~ADC_CR2_ADON;
DMA2_Stream0->CR &= ~DMA_SxCR_EN;
DMA2_Stream0->M0AR = (uint32_t)adc_array;
DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;
DMA2_Stream0->CR = DMA_SxCR_PL_1 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_TCIE;
DMA2_Stream0->NDTR = length;
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
DMA2_Stream0->CR |= DMA_SxCR_EN;
ADC1->CR2 |= ADC_CR2_ADON | ADC_CR2_DMA;
if (!flag_to_set) {
while(!alt_flag);
if (alt_flag < 0)
ret_val = -alt_flag;
}
return ret_val;
}
void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, uint16_t *raw_source, uint32_t count)
@ -84,7 +196,7 @@ void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, ui
}
enum adc_p1000_error adc_pt1000_check_error()
enum adc_pt1000_error adc_pt1000_check_error()
{
return pt1000_error;
}
@ -94,9 +206,20 @@ void adc_pt1000_clear_error()
pt1000_error = ADC_PT1000_NO_ERR;
}
static void adc_pt1000_filter(uint16_t adc_value)
void adc_pt1000_disable()
{
pt1000_adc_raw_lf = (1-filter_alpha) * pt1000_adc_raw_lf + filter_alpha * (float)adc_value;
adc_pt1000_disable_adc();
adc_pt1000_stop_sample_frequency_timer();
filter_ready = false;
pt1000_res_raw_lf = 0.0f;
}
static inline __attribute__((optimize("O3"))) void adc_pt1000_filter(uint16_t adc_value)
{
if (!filter_ready && --filter_startup_cnt <= 0)
filter_ready = true;
pt1000_res_raw_lf = (1-filter_alpha) * pt1000_res_raw_lf + filter_alpha * ADC_TO_RES((float)adc_value);
}
void ADC_IRQHandler(void)
@ -111,13 +234,38 @@ void ADC_IRQHandler(void)
if (adc1_sr & ADC_SR_OVR) {
ADC1->SR &= ~ADC_SR_OVR;
pt1000_error = ADC_PT1000_OVERFLOW;
pt1000_error |= ADC_PT1000_OVERFLOW;
/* Disable ADC in case of overrrun*/
ADC1->CR2 &= ADC_CR2_ADON;
adc_pt1000_disable();
if (streaming_active) {
streaming_active = false;
*dma_flag_ptr = -1;
}
}
if (adc1_sr & ADC_SR_AWD) {
ADC1->SR &= ~ADC_SR_AWD;
pt1000_error = ADC_PT1000_WATCHDOG_ERROR;
pt1000_error |= ADC_PT1000_WATCHDOG_ERROR;
}
}
void DMA2_Stream0_IRQHandler()
{
uint32_t lisr;
lisr = DMA2->LISR;
DMA2->LIFCR = lisr;
if (lisr & DMA_LISR_TCIF0) {
*dma_flag_ptr = 1;
ADC1->CR2 &= ~ADC_CR2_DMA;
streaming_active = false;
}
if (lisr & DMA_LISR_TEIF0) {
*dma_flag_ptr = -2;
ADC1->CR2 &= ~ADC_CR2_DMA;
streaming_active = false;
}
}

View File

@ -19,7 +19,9 @@
#define ADC_PT1000_PIN 2
#define ADC_FILTER_STARTUP_CYCLES 200
#define ADC_FILTER_STARTUP_CYCLES 800
#define ADC_PT1000_SAMPLE_CNT_DELAY 2000
/**
* @brief Lower value for valid input range for PT1000 measurement
@ -35,7 +37,7 @@
*/
#define ADC_PT1000_UPPER_WATCHDOG 4000
enum adc_p1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=-1, ADC_PT1000_OVERFLOW=2};
enum adc_pt1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=(1UL<<0), ADC_PT1000_OVERFLOW=(1UL<<1)};
/**
* @brief This function sets up the ADC measurement fo the external PT1000 temperature sensor
@ -86,8 +88,7 @@ void adc_pt1000_get_resistance_calibration(float *offset, float *sensitivity_dev
* If the reistance calibration is enabled, this function applies the calculations of the raw reistance reading and
* returns the corrected value.
*
* If an ADC error is set, the status is negative. If the ADC is in streaming mode, the reistance reading is disabled and
* the status is 1. The status is 2 during the first measurements with a given filter setting. Technically, the resistance value is
* If an ADC error is set, the status is negative. The status is 2 during the first measurements with a given filter setting. Technically, the resistance value is
* correct but the filter is not stable yet.
* Use adc_pt1000_check_error to check the error and reinitialize the ADC.
*
@ -102,7 +103,6 @@ int adc_pt1000_get_current_resistance(float *resistance);
*
* Streaming is done using DMA2 Stream0.
* This function is used for gathering fullspeed sampling data for external interfaces or calibration
* During streaming, the adc_pt1000_get_current_resistance() function will return an error
*
* @param adc_array Array to stream data to
* @param length Amount of data points to be measured
@ -118,8 +118,10 @@ void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, ui
*
* In case of an error, it may be necessary to call adc_pt1000_setup_meas() again in order to recover from the error
*/
enum adc_p1000_error adc_pt1000_check_error();
enum adc_pt1000_error adc_pt1000_check_error();
void adc_pt1000_clear_error();
void adc_pt1000_disable();
#endif // __ADCMEAS_H__

View File

@ -7,120 +7,39 @@
#include <stm32f4xx.h>
#include <systick.h>
//#include <arm_math.h>
#include <stm32-gpio-macros.h>
#include <system_stm32f4xx.h>
#include <stdlib.h>
#include <string.h>
#include <adc-meas.h>
#define OUTPUT(pin) (0b01 << (pin * 2))
#define ANALOG(pin) (0x03 << (pin * 2))
struct adc_conversions {
uint16_t pa2_raw;
uint16_t ref_raw;
uint16_t temp_raw;
uint16_t vbat_raw;
};
static volatile struct adc_conversions adc_results;
volatile uint64_t sample_count = 0;
volatile uint8_t new_data = 0;
void DMA2_Stream0_IRQHandler()
static void setup_nvic_priorities()
{
uint32_t lisr;
lisr = DMA2->LISR;
DMA2->LIFCR = lisr;
if (lisr & DMA_LISR_TCIF0) {
if (new_data)
new_data = 2;
new_data = 1;
sample_count++;
GPIOB->ODR ^= (1<<3);
}
/* No sub priorities */
NVIC_SetPriorityGrouping(2);
/* Setup Priorities */
NVIC_SetPriority(ADC_IRQn, 1);
NVIC_SetPriority(DMA2_Stream0_IRQn, 2);
}
void setup_dma(void *dest, size_t size)
{
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
DMA2_Stream0->M0AR = (uint32_t)dest;
DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;
DMA2_Stream0->CR = DMA_SxCR_PL_1 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC |
DMA_SxCR_CIRC | DMA_SxCR_TCIE;
DMA2_Stream0->NDTR = size;
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
DMA2_Stream0->CR |= DMA_SxCR_EN;
new_data = 0;
}
float ext_lf_corr;
float temp_lf_corr;
float ref_lf;
float vdd_calculated = 3.3f;
float vbat_lf_corr;
static void setup_timers(void)
{
}
static float pt1000_value;
static volatile int pt1000_value_status;
int main() {
struct adc_conversions working;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
__DSB();
GPIOB->MODER = OUTPUT(2) | OUTPUT(3);
GPIOA->MODER |= ANALOG(2);
GPIOB->ODR |= (1<<2);
ADC1->SMPR2 = (7U<<(3*2));
ADC1->SMPR1 = (7U<<18) | (7U<<21) | (7U<<24);
ADC1->SQR1 = (2<<20);
ADC1->SQR3 = (2<<0) | (16<<(5*2)) | (17<<(5*1));
ADC->CCR |= (0x2<<16) | ADC_CCR_TSVREFE;
ADC1->CR1 = ADC_CR1_SCAN;
ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CONT | ADC_CR2_DMA | ADC_CR2_DDS;
//while(1);
setup_nvic_priorities();
systick_setup();
setup_dma(&adc_results, 3);
//setup_dma(&adc_results, 3);
adc_pt1000_setup_meas();
ADC1->CR2 |= ADC_CR2_SWSTART;
while(1) {
if (!new_data) {
continue;
}
memcpy(&working, &adc_results, sizeof(adc_results));
new_data = 0;
//ref_lf = 0.995f * ref_lf + 0.005f * (float)working.ref_raw;
//vdd_calculated = ((1.21f * 4096)/ ref_lf);
//temp_lf_corr = 0.99f * temp_lf_corr + 0.01 * (float)working.temp_raw * vdd_calculated / 2.495f;
ext_lf_corr = 0.995f * ext_lf_corr + 0.005f * (float)working.pa2_raw / 4096 * 2500.0f; // * vdd_calculated / 2.495f;
//vbat_lf_corr = 0.99 * vbat_lf_corr + 0.01 * (float)working.vbat_raw / 4096 * vdd_calculated * 2.0f;
pt1000_value_status = adc_pt1000_get_current_resistance(&pt1000_value);
}
}