From be0b5c8b06bbe88275f52c13bfc9face31620fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 19 Jan 2017 23:11:43 +0100 Subject: [PATCH] add status LEDs and timer to measure DDM100TC meter pulse --- main.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/main.c b/main.c index 39db399..94ac966 100644 --- a/main.c +++ b/main.c @@ -57,6 +57,26 @@ volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ti #define QUERY_PERIOD 10 /**< period in seconds to query meter measurements */ +/** @defgroup main_leds LED to indicate status + * @{ + */ +#define LED_HEARTBEAT_PORT A /**< port for heart beat LED (green, on on low) */ +#define LED_HEARTBEAT_PIN 5 /**< pin for heart beat LED (green, on on low) */ +#define LED_QUERY_PORT A /**< port for query LED (yellow, on on low) */ +#define LED_QUERY_PIN 6 /**< pin for query LED (yellow, on on low) */ +#define LED_SUBMIT_PORT A /**< port for submit LED (blue, on on low) */ +#define LED_SUBMIT_PIN 7 /**< pin for submit LED (blue, on on low) */ +/** @} */ + +/** @defgroup main_ddm100tc resources to capture pulses from DDM100TC electricity meter + * @{ + */ +#define DDM100TC_TIMER 4 /**< timer to measure time between pulses **/ +#define DDM100TC_PORT B /**< timer ipnut capture port (TIM4_CH1=PB6) **/ +#define DDM100TC_CAPTURE TIM4_CH1 /**< time input capture used to detect pulse **/ +volatile uint32_t ddm100tc_interval = 0; /**< last time interval between pulses **/ +volatile uint32_t ddm100tc_pulses = 0; /**< total number of pulses captured **/ +/** @} */ int _write(int file, char *ptr, int len) { @@ -243,7 +263,20 @@ void main(void) setbuf(stderr, NULL); // set standard error buffer to NULL to immediately print // minimal setup ready - printf("welcome to the STM32F1 CuVoodoo example code\n"); // print welcome message + printf("welcome to the spark abacus electricity monitoring system\n"); // print welcome message + + // setup LEDs + printf("setup status LEDs: "); + rcc_periph_clock_enable(RCC_GPIO(LED_HEARTBEAT_PORT)); // enable clock for LED + gpio_set_mode(GPIO(LED_HEARTBEAT_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_HEARTBEAT_PIN)); // set LED pin to 'output push-pull' + gpio_set(GPIO(LED_HEARTBEAT_PORT), GPIO(LED_HEARTBEAT_PIN)); // switch off LED per default + rcc_periph_clock_enable(RCC_GPIO(LED_QUERY_PORT)); // enable clock for LED + gpio_set_mode(GPIO(LED_QUERY_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_QUERY_PIN)); // set LED pin to 'output push-pull' + gpio_set(GPIO(LED_QUERY_PORT), GPIO(LED_QUERY_PIN)); // switch off LED per default + rcc_periph_clock_enable(RCC_GPIO(LED_SUBMIT_PORT)); // enable clock for LED + gpio_set_mode(GPIO(LED_SUBMIT_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_SUBMIT_PIN)); // set LED pin to 'output push-pull' + gpio_set(GPIO(LED_SUBMIT_PORT), GPIO(LED_SUBMIT_PIN)); // switch off LED per default + printf("OK\n"); // setup RTC printf("setup internal RTC: "); @@ -255,6 +288,33 @@ void main(void) uint32_t ticks_time = rtc_get_counter_val(); // get time from internal RTC (since first start/power up) printf("uptime: %02lu:%02lu:%02lu\n", ticks_time/(60*60), (ticks_time%(60*60))/60, (ticks_time%60)); // display time + // setup DDM100TC electricity meter + rcc_periph_clock_enable(RCC_GPIO(DDM100TC_PORT)); // enable clock for GPIO block + gpio_set_mode(GPIO_BANK_(DDM100TC_CAPTURE), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_(DDM100TC_CAPTURE)); // setup GPIO pin as input + gpio_clear(GPIO_BANK_(DDM100TC_CAPTURE), GPIO_(DDM100TC_CAPTURE)); // pull down since the meter will set VCC when pulsing + rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (timer capture) + rcc_periph_clock_enable(RCC_TIM(DDM100TC_TIMER)); // enable clock for timer block + timer_reset(TIM(DDM100TC_TIMER)); // reset timer state + timer_set_mode(TIM(DDM100TC_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up + timer_set_prescaler(TIM(DDM100TC_TIMER), 0xffff); // set the prescaler to the maximum ( 1/(72E6/(2**16))=0.91ms which is a good enough resolution for this purpose) + timer_set_ti1_ch1(TIM(DDM100TC_TIMER)); // connect TIMx_CH1 to TI1 (this depends on the input capture pin you selected) + timer_ic_set_input(TIM(DDM100TC_TIMER), TIM_IC1, TIM_IC_IN_TI1); // configure IC1 to use TI1 + timer_ic_set_filter(TIM(DDM100TC_TIMER), TIM_IC1, TIM_IC_CK_INT_N_8); // use 8 sample to filter input (remove noise) + timer_ic_set_filter(TIM(DDM100TC_TIMER), TIM_IC1, TIM_IC_DTF_DIV_32_N_8); + timer_ic_set_polarity(TIM(DDM100TC_TIMER), TIM_IC1, TIM_IC_RISING); // capture on rising edge + timer_ic_set_prescaler(TIM(DDM100TC_TIMER), TIM_IC1, TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse + timer_slave_set_trigger(TIM(DDM100TC_TIMER), TIM_SMCR_TS_TI1FP1); // set filtered TI1 as trigger + timer_slave_set_mode(TIM(DDM100TC_TIMER), TIM_SMCR_SMS_RM); // reinitialise counter on rising edge of trigger + timer_clear_flag(TIM(DDM100TC_TIMER), TIM_SR_UIF); // clear update (UEv) flag + timer_update_on_overflow(TIM(DDM100TC_TIMER)); // only use counter overflow as UEV source + timer_enable_irq(TIM(DDM100TC_TIMER), TIM_DIER_UIE); // enable update event interrupt + timer_clear_flag(TIM(DDM100TC_TIMER), TIM_SR_CC1IF); // clear input compare flag + timer_enable_irq(TIM(DDM100TC_TIMER), TIM_DIER_CC1IE); // enable capture interrupt + nvic_enable_irq(NVIC_TIM_IRQ(DDM100TC_TIMER)); // catch interrupt in service routine + timer_ic_enable(TIM(DDM100TC_TIMER), TIM_IC1); // enable capture + timer_set_counter(TIM(DDM100TC_TIMER), 0); // reset timer counter + timer_enable_counter(TIM(DDM100TC_TIMER)); // enable timer + // setup PZEM electricity meter printf("setup PZEM-004 electricity meter: "); sensor_pzem_setup(); // setup PZEM electricity meter @@ -291,6 +351,7 @@ void main(void) button_flag = false; // reset button flag char c = '\0'; // to store received character bool char_flag = false; // a new character has been received + led_on(); // indicate setup is complete // variables for PZEM-004T meter measurements struct sensor_pzem_measurement_t pzem_measurements[2][SENSOR_PZEM_MAX]; // PZEM-004T measurements (2 meters, all measurements) @@ -304,17 +365,8 @@ void main(void) while (true) { // infinite loop iwdg_reset(); // kick the dog -/* - while (usart_received) { // data received over UART - action = true; // action has been performed - led_toggle(); // toggle LED - c = usart_getchar(); // store receive character - char_flag = true; // notify character has been received - } -*/ while (cdcacm_received) { // data received over USB action = true; // action has been performed - led_toggle(); // toggle LED c = cdcacm_getchar(); // store receive character char_flag = true; // notify character has been received } @@ -464,7 +516,7 @@ void main(void) } while (rtc_internal_tick_flag) { // the internal RTC ticked rtc_internal_tick_flag = false; // reset flag - //led_toggle(); // toggle LED (good to indicate if main function is stuck). do not toggle onboard the LED on PC13 on the blue pill board since this heavily influences the RTC (by ~13%) + gpio_toggle(GPIO(LED_HEARTBEAT_PORT), GPIO(LED_HEARTBEAT_PIN)); // toggle heart beat LED to indicate if main function is stuck (do not toggle onboard the LED on PC13 on the blue pill board since this heavily influences the RTC) ticks_time = rtc_get_counter_val(); // copy time from internal RTC for processing action = true; // action has been performed if ((ticks_time%(60))==0) { // one minute passed @@ -472,6 +524,7 @@ void main(void) } if ((ticks_time%(QUERY_PERIOD))==0) { // query period passed printf("query meter measurements (%lu.%02lu:%02lu:%02lu)\n", ticks_time/(60*60*24), (ticks_time/(60*60))%24, (ticks_time%(60*60))/60, (ticks_time%60)); + gpio_clear(GPIO(LED_QUERY_PORT), GPIO(LED_QUERY_PIN)); // switch on query LED // start getting all PZEM-004T measurements from all meters pzem_meter = 0; // reset PZEM meter number pzem_measurement = 0; // reset PZEM measurement index @@ -485,6 +538,8 @@ void main(void) while (pzem_meter>=LENGTH(pzem_measurements) && sdm120_meter>=LENGTH(sdm120_measurements)) { // all measurements received for all meter action = true; // action has been performed printf("saving measurements to database: "); + gpio_set(GPIO(LED_QUERY_PORT), GPIO(LED_QUERY_PIN)); // switch off query LED + gpio_clear(GPIO(LED_SUBMIT_PORT), GPIO(LED_SUBMIT_PIN)); // switch off submit LED const char* pzem_strings[SENSOR_PZEM_MAX] = { "voltage,meter=PZEM-004T,phase=%u value=%.1f\n", "current,meter=PZEM-004T,phase=%u value=%.2f\n", @@ -585,6 +640,7 @@ void main(void) } } http_end(); // end HTTP request (don't care about the result) + gpio_set(GPIO(LED_SUBMIT_PORT), GPIO(LED_SUBMIT_PIN)); // switch off submit LED printf("OK\n"); } @@ -607,3 +663,21 @@ void rtc_isr(void) rtc_clear_flag(RTC_SEC); // clear flag rtc_internal_tick_flag = true; // notify to show new time } + +/** interrupt service routine called for DDM100TC timer */ +void TIM_ISR(DDM100TC_TIMER)(void) +{ + static uint32_t long_time = 0; // large value of time, compared to the 16 bits counters + if (timer_get_flag(TIM(DDM100TC_TIMER), TIM_SR_UIF)) { // overflow update event happened + timer_clear_flag(TIM(DDM100TC_TIMER), TIM_SR_UIF); // clear flag + long_time += 0x10000; // count timer overflow for large time value + } else if (timer_get_flag(TIM(DDM100TC_TIMER), TIM_SR_CC1IF)) { // pulse detected + long_time += TIM_CCR1(TIM(DDM100TC_TIMER)); // get time (reading also clears the flag) + if (long_time>90) { // pulse is 90ms long, thus a new pulse before this time is probably just noise) + ddm100tc_interval = long_time; + ddm100tc_pulses++; // increment the number of pulses detected + long_time = 0; // reset time (slave mode should also have reset the counter) + } + } +} +