add status LEDs and timer to measure DDM100TC meter pulse

This commit is contained in:
King Kévin 2017-01-19 23:11:43 +01:00
parent 405c243f5c
commit be0b5c8b06
1 changed files with 85 additions and 11 deletions

96
main.c
View File

@ -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)
}
}
}