diff --git a/stm-firmware/Makefile b/stm-firmware/Makefile index 9602d82..217687d 100644 --- a/stm-firmware/Makefile +++ b/stm-firmware/Makefile @@ -10,7 +10,7 @@ INCLUDEPATH = -Icmsis -Iinclude OBJDIR = obj target = reflow-controller LIBRARYPATH = -L. -Lmathlib -LIBRARIES = -larm_cortexM4lf_math +LIBRARIES = -larm_cortexM4lf_math -lm DEFINES = -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4 -DHSE_VALUE=8000000UL mapfile = memory-mapping @@ -41,6 +41,8 @@ CFILES += digio.c CFILES += stm-periph/unique-id.c +CFILES += calibration.c + DEFINES += -DDEBUGBUILD #TODO diff --git a/stm-firmware/adc-meas.c b/stm-firmware/adc-meas.c index 6050940..229243c 100644 --- a/stm-firmware/adc-meas.c +++ b/stm-firmware/adc-meas.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -11,13 +11,16 @@ static bool calibration_active; static float filter_alpha; static volatile float pt1000_res_raw_lf; static volatile bool filter_ready; -static volatile enum adc_pt1000_error pt1000_error; -static volatile uint8_t * volatile streaming_flag_ptr = NULL; +static volatile enum adc_pt1000_error pt1000_error = ADC_PT1000_INACTIVE; +static volatile int * volatile streaming_flag_ptr = NULL; static uint32_t filter_startup_cnt; static volatile float adc_pt1000_raw_reading_hf; -static volatile bool pt1000_measurement_active = false; static volatile uint16_t dma_sample_buffer[ADC_PT1000_DMA_AVG_SAMPLES]; +volatile float * volatile stream_buffer = NULL; +volatile uint32_t stream_count; +volatile uint32_t stream_pos; + #define ADC_TO_RES(adc) ((float)(adc) / 4096.0f * 2500.0f) static inline void adc_pt1000_stop_sample_frequency_timer() @@ -49,7 +52,7 @@ static inline void adc_pt1000_disable_adc() ADC1->CR2 &= ~ADC_CR2_ADON; DMA2_Stream0->CR = 0; - pt1000_measurement_active = false; + pt1000_error |= ADC_PT1000_INACTIVE; rcc_manager_disable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC1EN)); rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(ADC_PT1000_PORT_RCC_MASK)); @@ -88,7 +91,7 @@ static inline void adc_pt1000_enable_dma_stream() * Todo: Maybe use twice as big of a buffer and also use half-fill interrupt in order to prevent overruns */ DMA2_Stream0->CR = DMA_SxCR_PL_1 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | - DMA_SxCR_CIRC | DMA_SxCR_TCIE | DMA_SxCR_EN; + DMA_SxCR_CIRC | DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_EN; } static inline void adc_pt1000_disable_dma_stream() @@ -142,7 +145,7 @@ void adc_pt1000_setup_meas() adc_pt1000_setup_sample_frequency_timer(); - pt1000_measurement_active = true; + pt1000_error &= ~ADC_PT1000_INACTIVE; } void adc_pt1000_set_moving_average_filter_param(float alpha) @@ -201,17 +204,40 @@ return_value: return ret_val; } -/* -int adc_pt1000_stream_raw_value_to_memory(float *adc_array, uint32_t length, volatile uint8_t *flag_to_set) +int adc_pt1000_stream_raw_value_to_memory(volatile float *adc_array, uint32_t length, volatile int *flag_to_set) { - return -1; + int ret = 0; + + if (!flag_to_set) + return -1; + + stream_buffer = adc_array; + stream_count = length; + stream_pos = 0U; + + if (adc_array) { + *flag_to_set = 0; + streaming_flag_ptr = flag_to_set; + } else { + ret = -2; + } + + return ret; } void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, float *raw_source, uint32_t count) { + uint32_t i; + if (!resistance_dest) + resistance_dest = raw_source; + + if (!raw_source || !count) + return; + + for (i = 0; i < count; i++) + resistance_dest[i] = ADC_TO_RES(raw_source[i]); } -*/ enum adc_pt1000_error adc_pt1000_check_error() { @@ -220,7 +246,7 @@ enum adc_pt1000_error adc_pt1000_check_error() void adc_pt1000_clear_error() { - pt1000_error = ADC_PT1000_NO_ERR; + pt1000_error &= ~ADC_PT1000_OVERFLOW & ~ADC_PT1000_WATCHDOG_ERROR; } void adc_pt1000_disable() @@ -231,6 +257,7 @@ void adc_pt1000_disable() filter_ready = false; pt1000_res_raw_lf = 0.0f; + pt1000_error |= ADC_PT1000_INACTIVE; if (streaming_flag_ptr) { *streaming_flag_ptr = -3; @@ -283,10 +310,6 @@ void ADC_IRQHandler(void) pt1000_error |= ADC_PT1000_OVERFLOW; /* Disable ADC in case of overrrun*/ adc_pt1000_disable(); - if (streaming_flag_ptr) { - *streaming_flag_ptr = -1; - streaming_flag_ptr = NULL; - } } if (adc1_sr & ADC_SR_AWD) { @@ -295,6 +318,21 @@ void ADC_IRQHandler(void) } } +static void append_stream_buffer(float val) +{ + if (!stream_buffer || !streaming_flag_ptr) + return; + + if (stream_pos < stream_count) + stream_buffer[stream_pos++] = val; + + if (stream_pos >= stream_count) { + *streaming_flag_ptr = 1; + streaming_flag_ptr = NULL; + } + +} + void DMA2_Stream0_IRQHandler() { uint32_t lisr; @@ -308,13 +346,15 @@ void DMA2_Stream0_IRQHandler() adc_val = adc_pt1000_dma_avg_pre_filter(); adc_pt1000_raw_reading_hf = adc_val; + if (streaming_flag_ptr) + append_stream_buffer(adc_val); + /* Call moving average filter */ adc_pt1000_filter(adc_val); } if (lisr & DMA_LISR_TEIF0) { - /* Wait for watchdog to kick in */ - while(1); + adc_pt1000_disable(); } } diff --git a/stm-firmware/calibration.c b/stm-firmware/calibration.c new file mode 100644 index 0000000..a7f64dc --- /dev/null +++ b/stm-firmware/calibration.c @@ -0,0 +1,88 @@ +/* Reflow Oven Controller + * + * Copyright (C) 2020 Mario Hüttel + * + * This file is part of the reflow Oven Controller Project. + * + * The reflow oven controller is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * GDSII-Converter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GDSII-Converter. If not, see . + */ + +#include +#include +#include +#include +#include + +void calibration_calculate(float low_measured, float low_setpoint, float high_measured, float high_setpoint, + float *sens_deviation, float *sens_corrected_offset) +{ + if (!sens_deviation || !sens_corrected_offset) + return; + + float delta_y; + float delta_x; + float sens_corr_mult; + + delta_y = high_measured - low_measured; + delta_x = high_setpoint - low_setpoint; + + sens_corr_mult = delta_x / delta_y; + *sens_deviation = sens_corr_mult - 1.0f; + + *sens_corrected_offset = low_setpoint - low_measured * sens_corr_mult; +} + + + +int calibration_acquire_data(float *mu, float *sigma, uint32_t count) +{ + int status; + float *stream_mem; + + static volatile int flag = 0; + + if (!mu || !sigma || !count) + return -1000; + + stream_mem = (float *)calloc(count, sizeof(float)); + if (!stream_mem) + return -2; + + /* Clear errors of PT1000 reading */ + adc_pt1000_clear_error(); + + status = adc_pt1000_stream_raw_value_to_memory(stream_mem, count, &flag); + if (status) + return status; + + /* Wait for data to be transferred */ + while (flag == 0); + + if (flag != 1) { + /* Error */ + return -1; + } + + /* Convert the stream memory to Ohm readings */ + adc_pt1000_convert_raw_value_array_to_resistance(NULL, stream_mem, count); + + /* Do not compute std-deviation. Too imprecise + * arm_std_f32(stream_mem, count, sigma); + */ + *sigma = 0; + arm_mean_f32(stream_mem, count, mu); + + free(stream_mem); + + return 0; +} diff --git a/stm-firmware/include/reflow-controller/adc-meas.h b/stm-firmware/include/reflow-controller/adc-meas.h index 1419890..db42c02 100644 --- a/stm-firmware/include/reflow-controller/adc-meas.h +++ b/stm-firmware/include/reflow-controller/adc-meas.h @@ -39,7 +39,7 @@ */ #define ADC_PT1000_UPPER_WATCHDOG 4000U -enum adc_pt1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=(1UL<<0), ADC_PT1000_OVERFLOW=(1UL<<1)}; +enum adc_pt1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=(1UL<<0), ADC_PT1000_OVERFLOW=(1UL<<1), ADC_PT1000_INACTIVE = (1UL<<2)}; /** * @brief This function sets up the ADC measurement fo the external PT1000 temperature sensor @@ -102,16 +102,12 @@ int adc_pt1000_get_current_resistance(float *resistance); /** * @brief Stream the raw ADC data to an array in memory. - * - * Streaming is done using DMA2 Stream0. - * This function is used for gathering fullspeed sampling data for external interfaces or calibration - * * @param adc_array Array to stream data to * @param length Amount of data points to be measured * @param flag_to_set This flag is set to 1 once the data has been measured and is transferred. A negative value indicates an error * @return 0 if measurement could be started */ -int adc_pt1000_stream_raw_value_to_memory(float *adc_array, uint32_t length, volatile uint8_t *flag_to_set); +int adc_pt1000_stream_raw_value_to_memory(volatile float *adc_array, uint32_t length, volatile int *flag_to_set); void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, float *raw_source, uint32_t count); diff --git a/stm-firmware/include/reflow-controller/calibration.h b/stm-firmware/include/reflow-controller/calibration.h new file mode 100644 index 0000000..3160309 --- /dev/null +++ b/stm-firmware/include/reflow-controller/calibration.h @@ -0,0 +1,32 @@ +/* Reflow Oven Controller + * + * Copyright (C) 2020 Mario Hüttel + * + * This file is part of the reflow Oven Controller Project. + * + * The reflow oven controller is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * GDSII-Converter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GDSII-Converter. If not, see . + */ + +#ifndef __CALIBRATION_H__ +#define __CALIBRATION_H__ + +#define CALIBRATION_MAX_STDDEV_OHM 1.0f + +#include + +void calibration_calculate(float low_measured, float low_setpoint, float high_measured, float high_setpoint, + float *sens_deviation, float *sens_corrected_offset); + +int calibration_acquire_data(float *mu, float *sigma, uint32_t count); + +#endif /* __CALIBRATION_H__ */ diff --git a/stm-firmware/main.c b/stm-firmware/main.c index 418c08e..b6c9d10 100644 --- a/stm-firmware/main.c +++ b/stm-firmware/main.c @@ -45,6 +45,9 @@ int main() shell_handle = shell_init(); + /* Try random calibration */ + adc_pt1000_set_resistance_calibration(-0.8f, 0.0f, true); + while(1) { pt1000_value_status = adc_pt1000_get_current_resistance(&pt1000_value); diff --git a/stm-firmware/shell.c b/stm-firmware/shell.c index 164b337..fe6251c 100644 --- a/stm-firmware/shell.c +++ b/stm-firmware/shell.c @@ -7,6 +7,7 @@ #include #include #include +#include #ifndef GIT_VER #define GIT_VER "VERSION NOT SET" @@ -103,22 +104,25 @@ static shellmatta_retCode_t shell_cmd_pt1000_res(const shellmatta_handle_t han (void)length; float resistance; int pt1000_status; - const char *display_status; enum adc_pt1000_error pt1000_flags; - const char *status_text[] = {"VALID", "WATCHDOG", "DATA-OVERFLOW", "UNSTABLE"}; + char display_status[100]; + display_status[0] = 0; - display_status = status_text[0]; pt1000_status = adc_pt1000_get_current_resistance(&resistance); if (pt1000_status == 2) { - display_status = status_text[3]; + strcat(display_status, " UNSTABLE "); } else if (pt1000_status) { pt1000_flags = adc_pt1000_check_error(); - if (pt1000_flags & ADC_PT1000_WATCHDOG_ERROR) - display_status = status_text[1]; + if (pt1000_flags & ADC_PT1000_INACTIVE) + strcat(display_status, " DEACTIVATED "); + else if (pt1000_flags & ADC_PT1000_WATCHDOG_ERROR) + strcat(display_status, " WATCHDOG "); else if (pt1000_flags & ADC_PT1000_OVERFLOW) - display_status = status_text[2]; + strcat(display_status, " OVERFLOW "); + } else { + strcpy(display_status, "VALID"); } shellmatta_printf(handle, "PT1000 resistance: %.2f Ohm [%s]\r\n", resistance, display_status); @@ -147,6 +151,17 @@ static shellmatta_retCode_t shell_cmd_uptime(const shellmatta_handle_t handle, shellmatta_printf(handle, "Uptime: %llu secs", global_tick_ms/1000); return SHELLMATTA_OK; } + +static shellmatta_retCode_t shell_cmd_acquire_val(const shellmatta_handle_t handle, + const char *arguments, + uint32_t length) +{ + float mu, sigma; + + calibration_acquire_data(&mu, &sigma, 128U); + shellmatta_printf(handle, "mu: %.2f\r\nsigma: %.3f\r\n", mu, sigma); + return SHELLMATTA_OK; +} //typedef struct shellmatta_cmd //{ // char *cmd; /**< command name */ @@ -157,7 +172,7 @@ static shellmatta_retCode_t shell_cmd_uptime(const shellmatta_handle_t handle, // struct shellmatta_cmd *next; /**< pointer to next command or NULL */ //} shellmatta_cmd_t; -static shellmatta_cmd_t cmd[6] = { +static shellmatta_cmd_t cmd[7] = { { .cmd = "version", .cmdAlias = "ver", @@ -204,8 +219,16 @@ static shellmatta_cmd_t cmd[6] = { .helpText = "Get uptime in seconds", .usageText = "", .cmdFct = shell_cmd_uptime, - .next = NULL, + .next = &cmd[6], }, + { + .cmd = "pt1000-acquire", + .cmdAlias = "ptac", + .helpText = "Acquire 128 samples", + .usageText = "", + .cmdFct = shell_cmd_acquire_val, + .next = NULL, + } }; shellmatta_handle_t shell_init(void) diff --git a/stm-firmware/syscalls.c b/stm-firmware/syscalls.c index d8fff8a..40d8f12 100644 --- a/stm-firmware/syscalls.c +++ b/stm-firmware/syscalls.c @@ -1,4 +1,8 @@ #include +#include +#include +#include + char* _sbrk(int incr) { @@ -12,12 +16,16 @@ char* _sbrk(int incr) } prev_heap_end = heap_end; + + if (heap_end + incr > &heap_top) { - return 0; + errno = ENOMEM; + return (char *)-1; } heap_end += incr; - return (char*) prev_heap_end; + + return (char *) prev_heap_end; } int _isatty(int fd)