273 lines
7.5 KiB
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;
|
|
}
|