flow_meter/src/main.c

273 lines
7.5 KiB
C

/* Copyright (c) 2016, Jan Wiśniewski <vuko@hackerspace.pl>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*/
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/dbgmcu.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/usb/cdc.h>
#include <libopencm3/usb/usbd.h>
#include <libopencmsis/core_cm3.h>
#include <stdint.h>
#include <stdio.h>
#include "fifo.h"
#include "iobuffers.h"
#include "usb.h"
/* connected to white cables from flow meters
* 1300 impulses (LO/HI cycle) per liter (TODO make sure)
* external pull-ups are needed
*/
#define INPUT1_PIN GPIO8
#define INPUT2_PIN GPIO9
#define INPUTS_PORT GPIOB
/* buzzer output pin
* HIGH - buzzer on
* LOW - buzzer off
*/
#define BUZZER_PIN GPIO12
#define BUZZER_PORT GPIOB
uint8_t tx_buffer[1024];
uint8_t rx_buffer[1024];
fifo tx_fifo;
fifo rx_fifo;
int main(void);
#ifdef STDIO_USB
usbd_device* usbd_dev;
void usb_hp_can_tx_isr(void){
usbd_poll(usbd_dev);
}
void usb_lp_can_rx0_isr(void){
usbd_poll(usbd_dev);
}
#endif
#ifdef STDIO_UART
void usart1_isr(void) {
uint8_t data;
/* RXNE interrupt handling */
if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
((USART_SR(USART1) & USART_SR_RXNE) != 0)) {
data = usart_recv(USART1);
if(!fifoFull(&rx_fifo))
fifoPush(&rx_fifo, data);
}
/* TXE interrupt handling */
if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) &&
((USART_SR(USART1) & USART_SR_TXE) != 0)) {
if(!fifoEmpty(&tx_fifo)){
data = fifoPop(&tx_fifo);
usart_send(USART1, data);
} else {
/* disable TXE interrupt if buffer empty */
USART_CR1(USART1) &= ~USART_CR1_TXEIE;
}
}
}
#endif
/* main routine */
void tim2_isr(void)
{
enum flow_state_t {
INIT, // initial state emit four 1 second beeps
NORMAL, // flow is ok
WARNING, // low flow for less than 4s, emit 0.5 second beeps
ERROR // low flow for more than 4s, emit continuous beep
};
static enum flow_state_t flow_state = INIT;
static uint32_t cycles = 0; // cycles from last second
static uint16_t seconds = 0; // seconds from last state change
static uint8_t previous[2]; // input values from last cycle
static uint16_t impulses[2]; // number of impulses from last second
uint8_t alarm; // should buzzer be on?
const uint32_t minimal_value = 2; // minimal number of impulses per second
const uint16_t cycles_per_second = 1000;
uint8_t good = (impulses[0] >= minimal_value) &&
(impulses[1] > minimal_value);
cycles = cycles + 1;
if (cycles >= cycles_per_second){
cycles = 0;
seconds = seconds + 1;
}
if(cycles == 0){
printf("impulses %u %u\r\n",impulses[0],impulses[1]);
switch(flow_state){
case INIT:
if(seconds >= 4){
flow_state = NORMAL;
seconds = 0;
}
break;
case NORMAL:
if(!good){
flow_state = WARNING;
seconds = 0;
}
break;
case WARNING:
if(good){
flow_state = NORMAL;
seconds = 0;
}
else if( seconds >= 4){
flow_state = ERROR;
seconds = 0;
}
break;
case ERROR:
if(good){
flow_state = NORMAL;
seconds = 0;
}
break;
}
impulses[0] = 0;
impulses[1] = 0;
}
alarm = 0;
switch(flow_state){
case INIT:
if(seconds % 2 == 0) alarm = 1;
break;
case NORMAL:
alarm = 0;
break;
case WARNING:
if(cycles > (cycles_per_second/2)) alarm = 1;
break;
case ERROR:
alarm = 1;
break;
}
uint16_t port_in;
uint8_t current[2];
port_in = gpio_port_read(INPUTS_PORT);
current[0] = (port_in & INPUT1_PIN) != 0;
current[1] = (port_in & INPUT2_PIN) != 0;
/* detect rising edge */
uint8_t input_no;
for (input_no=0; input_no<2; input_no+=1){
if (current[input_no] && !previous[input_no]){
impulses[input_no] += 1;
}
previous[input_no] = current[input_no];
}
if(alarm){
gpio_set(BUZZER_PORT, BUZZER_PIN);
}
else {
gpio_clear(BUZZER_PORT, BUZZER_PIN);
}
TIM_SR(TIM2) &= ~TIM_SR_UIF; /* Clear interrrupt flag. */
}
int main(){
// enable dubbuger during sleep modes
//DBGMCU_CR |= DBGMCU_CR_SLEEP | DBGMCU_CR_STOP | DBGMCU_CR_STANDBY;
/* clocks init */
rcc_clock_setup_in_hse_8mhz_out_72mhz();
//rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_AFIO);
rcc_periph_clock_enable(RCC_TIM2);
/* init data structures */
fifoInit(&tx_fifo, tx_buffer, 1024);
fifoInit(&rx_fifo, rx_buffer, 1024);
#ifdef STDIO_UART
/* usart configuration */
rcc_periph_clock_enable(RCC_USART1);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX);
gpio_set_mode(GPIOA, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_FLOAT, GPIO_USART1_RX);
usart_set_baudrate(USART1, 115200);
usart_set_databits(USART1, 8);
usart_set_stopbits(USART1, USART_STOPBITS_1);
usart_set_parity(USART1, USART_PARITY_NONE);
usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
usart_set_mode(USART1, USART_MODE_TX_RX);
USART_CR1(USART1) |= USART_CR1_RXNEIE; // enable interrupt
usart_enable(USART1);
#endif
#ifdef STDIO_USB
/* usb configuration */
rcc_set_usbpre(RCC_CFGR_USBPRE_PLL_CLK_DIV1_5);
usbd_dev = usb_init();
usbd_poll(usbd_dev);
nvic_enable_irq(NVIC_USB_HP_CAN_TX_IRQ);
nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
nvic_set_priority(NVIC_USB_HP_CAN_TX_IRQ, 1);
nvic_set_priority(NVIC_USB_LP_CAN_RX0_IRQ, 1);
#endif
/* main timer configuration */
nvic_enable_irq(NVIC_TIM2_IRQ);
nvic_set_priority(NVIC_TIM2_IRQ, 1);
nvic_enable_irq(NVIC_USART1_IRQ);
TIM_CNT(TIM2) = 0; // timer start value
TIM_PSC(TIM2) = 71; // timer prescaller
TIM_ARR(TIM2) = 1000; // timer TOP
TIM_DIER(TIM2) |= TIM_DIER_UIE; // enable interrupt
TIM_CR1(TIM2) |= TIM_CR1_CEN; // start timer
gpio_set_mode(INPUTS_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN,
INPUT1_PIN | INPUT2_PIN);
gpio_set(INPUTS_PORT, INPUT1_PIN | INPUT2_PIN);
gpio_set_mode(BUZZER_PORT, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, BUZZER_PIN);
gpio_set(BUZZER_PORT, BUZZER_PIN);
SCB_SCR |= SCB_SCR_SLEEPONEXIT | SCB_SCR_SEVEONPEND;
while(1){
__WFI();
}
return 0;
}