main: config pin, IR out, shake wakeup
This commit is contained in:
parent
cc63108403
commit
eed88beebe
203
main.c
203
main.c
|
@ -1,5 +1,5 @@
|
|||
/* firmware template for STM8S microcontroller
|
||||
* Copyright (C) 2019-2020 King Kévin <kingkevin@cuvoodoo.info>
|
||||
/* firmware for STM8S003-based dachboden badge
|
||||
* Copyright (C) 2019-2022 King Kévin <kingkevin@cuvoodoo.info>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
@ -9,7 +9,41 @@
|
|||
#include "stm8s.h"
|
||||
#include "main.h"
|
||||
|
||||
// enable UART debug
|
||||
#define DEBUG 1
|
||||
|
||||
// pinout
|
||||
// on-board LED (sink on)
|
||||
#define LED_ONBOARD_PIN PB5
|
||||
#define LED_ONBOARD_PORT GPIO_PB
|
||||
// pin to power IR demodulator (source on)
|
||||
#define IRM_ON_PIN PA3
|
||||
#define IRM_ON_PORT GPIO_PA
|
||||
// IR demodulator output pin
|
||||
#define IRM_OUT_PIN PC7 // TIM1_CH2
|
||||
#define IRM_OUT_PORT GPIO_PC
|
||||
// RGB LED pins (sink on)
|
||||
#define LED_RED_PIN PD3 // TIM2_CH2
|
||||
#define LED_RED_PORT GPIO_PD
|
||||
#define LED_GREEN_PIN PD2 // TIM2_CH3
|
||||
#define LED_GREEN_PORT GPIO_PD
|
||||
#define LED_BLUE_PIN PC5 // TIM2_CH1
|
||||
#define LED_BLUE_PORT GPIO_PC
|
||||
// IR LED pin (source on)
|
||||
#define LED_IR_PIN PC4 // TIM1_CH4
|
||||
#define LED_IR_PORT GPIO_PC
|
||||
// UV LED pin (source on)
|
||||
#define LED_UV_PIN PC3 // TIM1_CH3
|
||||
#define LED_UV_PORT GPIO_PC
|
||||
// vibration sensor input (high on vibration)
|
||||
#define SHAKE_PIN PA2
|
||||
#define SHAKE_PORT GPIO_PA
|
||||
#define SHAKE_IRQ IRQ_EXTI0 // port A
|
||||
|
||||
// number of vibrations registered
|
||||
static volatile uint16_t shake_count = 0;
|
||||
// number of time counts (+1 @ 488 Hz)
|
||||
static volatile uint32_t time_count = 0;
|
||||
|
||||
// blocking wait (in 10 us steps, up to UINT32_MAX / 10)
|
||||
static void wait_10us(uint32_t us10)
|
||||
|
@ -18,6 +52,44 @@ static void wait_10us(uint32_t us10)
|
|||
while (us10--); // burn energy
|
||||
}
|
||||
|
||||
void putc(char c)
|
||||
{
|
||||
(void)c;
|
||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
#if DEBUG
|
||||
while (!UART1->SR.fields.TXE); // wait until TX buffer is empty
|
||||
UART1->DR.reg = c; // put character in buffer to be transmitted
|
||||
// don't wait until the transmission is complete
|
||||
#endif
|
||||
}
|
||||
|
||||
void puts(const char* s)
|
||||
{
|
||||
if (NULL == s) {
|
||||
return;
|
||||
}
|
||||
while (*s) {
|
||||
putc(*s++);
|
||||
}
|
||||
}
|
||||
|
||||
void putn(uint8_t n)
|
||||
{
|
||||
n &= 0x0f; // ensure it's a nibble
|
||||
if (n < 0xa) {
|
||||
n += '0';
|
||||
} else {
|
||||
n = 'a' + (n - 0x0a);
|
||||
}
|
||||
putc(n);
|
||||
}
|
||||
|
||||
void puth(uint8_t h)
|
||||
{
|
||||
putn(h >> 4);
|
||||
putn(h & 0x0f);
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
sim(); // disable interrupts (while we reconfigure them)
|
||||
|
@ -26,24 +98,133 @@ void main(void)
|
|||
CLK->CKDIVR.fields.CPUDIV = CLK_CKDIVR_CPUDIV_DIV0; // don't divide CPU frequency to 16 MHz
|
||||
while (!CLK->ICKR.fields.HSIRDY); // wait for internal oscillator to be ready
|
||||
|
||||
// TODO only enable clock for peripherals used (PCG)
|
||||
|
||||
// configure LED pin
|
||||
LED_ONBOARD_PORT->ODR.reg |= LED_ONBOARD_PIN; // switch LED off
|
||||
LED_ONBOARD_PORT->CR1.reg &= ~LED_ONBOARD_PIN; // use as open-drain
|
||||
LED_ONBOARD_PORT->DDR.reg |= LED_ONBOARD_PIN; // switch pin to output
|
||||
|
||||
// configure option bytes
|
||||
// disable DATA (e.g. option byte) write protection
|
||||
if (0 == (FLASH_IAPSR & FLASH_IAPSR_DUL)) {
|
||||
FLASH_DUKR = FLASH_DUKR_KEY1;
|
||||
FLASH_DUKR = FLASH_DUKR_KEY2;
|
||||
}
|
||||
FLASH_CR2 |= FLASH_CR2_OPT; // set option bytes programming
|
||||
FLASH_NCR2 &= ~FLASH_NCR2_NOPT; // set option bytes programming
|
||||
|
||||
OPT->OPT2.fields.AFR0 = 1; // remap TIM2_CH1 to PC5, TIM1_CH1 to C6, and TIM1_CH2 to C7
|
||||
OPT->NOPT2.fields.NAFR0 = 0; // set complementary option byte
|
||||
OPT->OPT2.fields.AFR1 = 1; // remap TIM2_CH3 to PD2
|
||||
OPT->NOPT2.fields.NAFR1 = 0; // set complementary option byte
|
||||
|
||||
while (!(FLASH_IAPSR & FLASH_IAPSR_EOP)); // wait for write to complete
|
||||
FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection
|
||||
|
||||
// configure UART for debug output
|
||||
UART1->CR1.fields.M = 0; // 8 data bits
|
||||
UART1->CR3.fields.STOP = 0; // 1 stop bit
|
||||
UART1->BRR2.reg = 0x0B; // set baud rate to 115200 (at 16 MHz)
|
||||
UART1->BRR1.reg = 0x08; // set baud rate to 115200 (at 16 MHz)
|
||||
UART1->CR2.fields.TEN = 1; // enable TX
|
||||
|
||||
// configure IR demodulator pin
|
||||
IRM_ON_PORT->ODR.reg &= ~IRM_ON_PIN; // switch IR demodulator off
|
||||
IRM_ON_PORT->ODR.reg |= IRM_ON_PIN; // switch IR demodulator on
|
||||
IRM_ON_PORT->CR1.reg |= IRM_ON_PIN; // use as push-pull
|
||||
IRM_ON_PORT->DDR.reg |= IRM_ON_PIN; // switch pin to output
|
||||
|
||||
// configure RGB LED
|
||||
LED_RED_PORT->ODR.reg |= LED_RED_PIN; // switch LED off
|
||||
LED_RED_PORT->CR1.reg &= ~LED_RED_PIN; // use as open-drain
|
||||
LED_RED_PORT->DDR.reg |= LED_RED_PIN; // use pin to output
|
||||
LED_GREEN_PORT->ODR.reg |= LED_GREEN_PIN; // switch LED off
|
||||
LED_GREEN_PORT->CR1.reg &= ~LED_GREEN_PIN; // use as open-drain
|
||||
LED_GREEN_PORT->DDR.reg |= LED_GREEN_PIN; // use pin to output
|
||||
LED_BLUE_PORT->ODR.reg |= LED_BLUE_PIN; // switch LED off
|
||||
LED_BLUE_PORT->CR1.reg &= ~LED_BLUE_PIN; // use as open-drain
|
||||
LED_BLUE_PORT->DDR.reg |= LED_BLUE_PIN; // use pin to output
|
||||
|
||||
/*
|
||||
// configure IR LED
|
||||
LED_IR_PORT->ODR.reg &= ~LED_IR_PIN; // switch LED off
|
||||
LED_IR_PORT->CR1.reg |= LED_IR_PIN; // use as push-pull
|
||||
LED_IR_PORT->DDR.reg |= LED_IR_PIN; // use pin to output
|
||||
*/
|
||||
|
||||
// configure UV LED
|
||||
LED_UV_PORT->ODR.reg &= ~LED_UV_PIN; // switch LED off
|
||||
LED_UV_PORT->CR1.reg |= LED_UV_PIN; // use as push-pull
|
||||
LED_UV_PORT->DDR.reg |= LED_UV_PIN; // use pin to output
|
||||
|
||||
// configure vibration sensor input
|
||||
SHAKE_PORT->CR1.reg &= ~SHAKE_PIN; // leave floating (pulled down externally)
|
||||
SHAKE_PORT->DDR.reg &= ~SHAKE_PIN; // set as input
|
||||
SHAKE_PORT->CR2.reg |= SHAKE_PIN; // enable external input
|
||||
EXTI->CR1.fields.PAIS = EXTI_RISING_EDGE; // interrupt when vibration is detected
|
||||
shake_count = 0; // reset counter
|
||||
|
||||
// use timer 4 (8-bit) as timeout counter
|
||||
TIM4->PSCR.fields.PSC = 7; // make it as slow as possible 16E6 / 2**7 = 125 kHz, / 256 = 488 Hz
|
||||
TIM4->CNTR.fields.CNT = 0; // reset counter
|
||||
TIM4->IER.fields.UIE = 1; // enable interrupt
|
||||
time_count = 0; // reset time counter
|
||||
TIM4->CR1.fields.URS = 1; // only update on overflow
|
||||
TIM4->CR1.fields.CEN = 1; // enable counter
|
||||
|
||||
// configure timer to modulate IR LEDs at 38 kHz
|
||||
TIM1->PSCRH.reg = 0; // set prescaler to get most precise 38 kHz
|
||||
TIM1->PSCRL.reg = 0; // 16E6/(0+1)/65536 = down to 244 Hz
|
||||
#define TIM1_PERIOD 421U // 16E6/(0+1)/38000
|
||||
TIM1->ARRH.reg = (TIM1_PERIOD >> 8); // set auto-reload register for 38 kHz period
|
||||
TIM1->ARRL.reg = (TIM1_PERIOD & 0xff); // 16E6/(0+1)/38000
|
||||
TIM1->CCR4H.reg = (TIM1_PERIOD / 3) >> 8; // set duty cycle to 33%
|
||||
TIM1->CCR4L.reg = (TIM1_PERIOD / 3 ) & 0xff; // set duty cycle to 33%
|
||||
TIM1->CCMR4.output_fields.OC4M = 6; // set PWM1 mode
|
||||
TIM1->CCMR4.output_fields.CC4S = 0; // use channel as output
|
||||
TIM1->CCER2.fields.CC4E = 1; // enable channel output
|
||||
TIM1->BKR.fields.MOE = 1; // enable outputs
|
||||
TIM1->EGR.fields.UG = 1; // transfer all registers
|
||||
TIM1->CR1.fields.CEN = 1; // enable counter to start PWM
|
||||
|
||||
/* don't use the AWU, else it will cause an active-halt instead of halt, using more power
|
||||
// configure auto-wakeup (AWU) to be able to refresh the watchdog
|
||||
// 128 kHz LSI used by default in option bytes CKAWUSEL
|
||||
// we skip measuring the LS clock frequency since there is no need to be precise
|
||||
AWU->TBR.fields.AWUTB = 10; // interval range: 128-256 ms
|
||||
AWU->APR.fields.APR = 0x3e; // set time to 256 ms
|
||||
AWU_CSR |= AWU_CSR_AWUEN; // enable AWU (start only when entering wait or active halt mode)
|
||||
AWU->CSR.fields.AWUEN = 1; // enable AWU (start only when entering wait or active halt mode)
|
||||
*/
|
||||
|
||||
/* don't use IWDG since it wakes up from HALT mode and uses (a little) power
|
||||
use WWDG instead
|
||||
// configure independent watchdog (very loose, just it case the firmware hangs)
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_ENABLE; // start watchdog
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_ACCESS; // allows changing the prescale
|
||||
IWDG->PR.fields.PR = IWDG_PR_DIV256; // set prescale to longest time (1.02s)
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
*/
|
||||
|
||||
rim(); // re-enable interrupts
|
||||
LED_ONBOARD_PORT->ODR.reg &= ~LED_ONBOARD_PIN; // switch LED on to indicate we are ready
|
||||
bool action = false; // if an action has been performed
|
||||
puts("ready\r\n");
|
||||
while (true) {
|
||||
putc('.');
|
||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
if (shake_count) {
|
||||
puts("vibrations: ");
|
||||
puth(shake_count);
|
||||
puts("\r\n");
|
||||
shake_count = 0; // reset count
|
||||
}
|
||||
if (time_count > 488 * 10) {
|
||||
puts("10s\r\n");
|
||||
time_count = 0; // reset counter
|
||||
halt();
|
||||
}
|
||||
if (action) { // something has been performed, check if other flags have been set meanwhile
|
||||
action = false; // clear flag
|
||||
} else { // nothing down
|
||||
|
@ -52,8 +233,22 @@ void main(void)
|
|||
}
|
||||
}
|
||||
|
||||
void awu(void) __interrupt(IRQ_AWU) // auto wakeup
|
||||
// auto wakeup
|
||||
void awu(void) __interrupt(IRQ_AWU)
|
||||
{
|
||||
volatile uint8_t awuf = AWU_CSR; // clear interrupt flag by reading it (reading is required, and volatile prevents compiler optimization)
|
||||
// let the main loop kick the dog
|
||||
}
|
||||
|
||||
// vibration sensor input
|
||||
void shake_isr(void) __interrupt(SHAKE_IRQ)
|
||||
{
|
||||
shake_count++; // register vibration
|
||||
}
|
||||
|
||||
// time counter
|
||||
void time_isr(void) __interrupt(IRQ_TIM4)
|
||||
{
|
||||
TIM4->SR.fields.UIF = 0; // clear flag
|
||||
time_count++; // remember overflow to count time
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue