@ -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,82 @@
# include "stm8s.h"
# include "main.h"
// enable UART debug
# define DEBUG 1
// EEPROM start address
# define EEPROM_ADDR 0x4000
// pinout
// 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 controlled by nMOS)
# 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 ;
// last time the badge was shook
static volatile uint32_t time_shake = 0 ;
// time after last shake to go to sleep, in seconds
# define REST_TIME (5 * 60U)
// period to share our color code, in seconds
# define SHARE_TIME (1U)
// period to enforce our color code, in seconds
# define MASTER_TIME (1U)
// time counts per us (1/(16E6/(3+1)) * 1000*1000 = 0.25 us)
# define NEC_TICKS_PER_US 4UL
// burst error margin in %
# define NEC_ERROR 15
// AGC burst length (9 ms)
# define NEC_AGC_BURST (9000 * NEC_TICKS_PER_US)
// AGC slot length (9 + 4.5 ms)
# define NEC_AGC_SLOT (NEC_AGC_BURST + (4500 * NEC_TICKS_PER_US))
// bit burst length (560 us)
# define NEC_BIT_BURST (560 * NEC_TICKS_PER_US)
// logical 0 slot length
# define NEC_0_SLOT (1125 * NEC_TICKS_PER_US)
// logical 1 slot length
# define NEC_1_SLOT (2250 * NEC_TICKS_PER_US)
# define NEC_AGC_SLOT_MIN (NEC_AGC_SLOT * (100 - NEC_ERROR) / 100U)
# define NEC_AGC_SLOT_MAX (NEC_AGC_SLOT * (100 + NEC_ERROR) / 100U)
# define NEC_0_SLOT_MIN (NEC_0_SLOT * (100 - NEC_ERROR) / 100U)
# define NEC_0_SLOT_MAX (NEC_0_SLOT * (100 + NEC_ERROR) / 100U)
# define NEC_1_SLOT_MIN (NEC_1_SLOT * (100 - NEC_ERROR) / 100U)
# define NEC_1_SLOT_MAX (NEC_1_SLOT * (100 + NEC_ERROR) / 100U)
// bit position in the NEC message (-2 = invalid, -1 = AGC)
static volatile int8_t nec_bit = - 2 ;
// complete NEC message
static volatile uint8_t nec_msg [ 4 ] = { 0 } ;
// flag set if NEC message has been received
static volatile bool nec_flag = false ;
// set when data is received over UART
static volatile char uart_c = 0 ;
// blocking wait (in 10 us steps, up to UINT32_MAX / 10)
static void wait_10us ( uint32_t us10 )
@ -18,32 +93,486 @@ 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 ) ;
}
// ASCII to nibble (0xff if invalid)
static uint8_t a2n ( char c )
{
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
return c - ' 0 ' ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
return c - ' a ' + 0xa ;
} else if ( c > = ' A ' & & c < = ' F ' ) {
return c - ' A ' + 0xa ;
} else {
return 0xff ;
}
}
// set duty cycle of red LED
void led_red ( uint16_t bightness )
{
TIM2 - > CCR2H . reg = ( bightness > > 8 ) ; // set duty cycle
TIM2 - > CCR2L . reg = ( bightness > > 0 ) ; // set duty cycle
}
// set duty cycle of green LED
void led_green ( uint16_t bightness )
{
TIM2 - > CCR3H . reg = ( bightness > > 8 ) ; // set duty cycle
TIM2 - > CCR3L . reg = ( bightness > > 0 ) ; // set duty cycle
}
// set duty cycle of blue LED
void led_blue ( uint16_t bightness )
{
TIM2 - > CCR1H . reg = ( bightness > > 8 ) ; // set duty cycle
TIM2 - > CCR1L . reg = ( bightness > > 0 ) ; // set duty cycle
}
void led_rgb ( uint8_t * rgb )
{
if ( NULL = = rgb ) {
return ;
}
led_red ( rgb [ 0 ] < < 8 ) ;
led_red ( rgb [ 0 ] < < 8 ) ;
led_green ( rgb [ 1 ] < < 8 ) ;
led_green ( rgb [ 1 ] < < 8 ) ;
led_blue ( rgb [ 2 ] < < 8 ) ;
// no idea why, but if I don't do it a second time the blue is a bit on when switched off
// it is not about the register order or preload
led_blue ( rgb [ 2 ] < < 8 ) ;
}
// configure timer to capture IR NEC codes
static void timer_ir_in ( void )
{
TIM1 - > CR1 . reg = 0 ; // disable counter before reconfiguring it
TIM1 - > IER . reg = 0 ; // reset interrupts
TIM1 - > BKR . reg = 0 ; // reset register
TIM1 - > CCER1 . reg = 0 ; // reset register
TIM1 - > CCER2 . reg = 0 ; // reset register
TIM1 - > PSCRH . reg = 0 ; // set prescaler to get most precise 9+4.5 ms
TIM1 - > PSCRL . reg = 3 ; // 16E6/(3+1)/65536 = up to 16 ms
TIM1 - > ARRH . reg = 0xff ; // let it count to the end
TIM1 - > ARRL . reg = 0xff ; // an overflow means the signal is corrupted
TIM1 - > CCMR1 . input_fields . CC1S = 1 ; // configure channel as input and map CH1 to TI1FP1
TIM1 - > CCER1 . fields . CC1P = 1 ; // trigger on a low level or falling edge of TI1F
TIM1 - > CCMR2 . input_fields . CC2S = 2 ; // configure channel as input and map CH2 to TI1FP2
TIM1 - > CCER1 . fields . CC2P = 0 ; // trigger on a high level or rising edge of TI1F
TIM1 - > SMCR . fields . TS = 5 ; // set trigger to filtered timer input 1 (TI1FP1)
// don't filter the external trigger
TIM1 - > SMCR . fields . SMS = 4 ; // reset on trigger
TIM1 - > CCER1 . fields . CC1E = 1 ; // enable channel 1 for input capture
TIM1 - > CCER1 . fields . CC2E = 1 ; // enable channel 2 for input capture
TIM1 - > IER . fields . CC1IE = 1 ; // enable interrupt for channel
TIM1 - > IER . fields . CC2IE = 1 ; // enable interrupt for channel
TIM1 - > IER . fields . UIE = 1 ; // enable update interrupt
TIM1 - > CR1 . fields . URS = 1 ; // only update on overflow
TIM1 - > SR1 . reg = 0 ; // clear all flags
TIM1 - > CNTRL . reg = 0 ; // reset counter
TIM1 - > CNTRH . reg = 0 ; // reset counter
TIM1 - > EGR . fields . UG = 1 ; // transfer all registers
TIM1 - > CR1 . fields . CEN = 1 ; // enable counter to start capture
nec_bit = - 2 ; // invalidate current packet
IRM_ON_PORT - > ODR . reg | = IRM_ON_PIN ; // switch IR demodulator on
}
# define TIM1_PERIOD 421U // 16E6/(0+1)/38000
// configure timer to transmit IR burst at 38 kHz
static void timer_ir_out ( void )
{
IRM_ON_PORT - > ODR . reg & = ~ IRM_ON_PIN ; // switch IR demodulator off
TIM1 - > CR1 . reg = 0 ; // disable counter before reconfiguring it
TIM1 - > CCER1 . reg = 0 ; // reset register
TIM1 - > CCER2 . reg = 0 ; // reset register
TIM1 - > IER . reg = 0 ; // reset interrupts
TIM1 - > PSCRH . reg = 0 ; // set prescaler to get most precise 38 kHz
TIM1 - > PSCRL . reg = 0 ; // 16E6/(0+1)/65536 = down to 244 Hz
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 - > SR1 . reg = 0 ; // clear all flags
TIM1 - > CNTRL . reg = 0 ; // reset counter
TIM1 - > CNTRH . reg = 0 ; // reset counter
TIM1 - > CR1 . fields . OPM = 1 ; // send one pulse at a time
TIM1 - > EGR . fields . UG = 1 ; // transfer all registers
// don't enable timer yet
}
// transmit IR pulses
static void nec_pulse ( uint16_t pulses , bool mark )
{
if ( mark ) {
TIM1 - > CCR4H . reg = ( TIM1_PERIOD / 3 ) > > 8 ; // set duty cycle to 33%
TIM1 - > CCR4L . reg = ( TIM1_PERIOD / 3 ) & 0xff ; // set duty cycle to 33%
} else {
TIM1 - > CCR4H . reg = 0 ; // set duty cycle to 0%
TIM1 - > CCR4L . reg = 0 ; // set duty cycle to 0%
}
while ( pulses - - ) {
TIM1 - > CR1 . fields . CEN = 1 ; // enable counter to start PWM for 1 pulse
while ( TIM1 - > CR1 . fields . CEN ) ; // wait until pulse completes
}
// ensure we are off at the end
TIM1 - > CCR4H . reg = 0 ; // set duty cycle to 0%
TIM1 - > CCR4L . reg = 0 ; // set duty cycle to 0%
}
// transmit 4 byte NEC code over IR (LSb first)
static void nec_transmit ( uint32_t code )
{
if ( NULL = = code ) {
return ;
}
timer_ir_out ( ) ; // configure to transmit pulses
sim ( ) ; // disable interrupts to keep timings tight
// all time are hand tuned
nec_pulse ( 333 , true ) ; // send AGC burst, 9 ms
nec_pulse ( 166 , false ) ; // AGC space, 4.5 ms
// transmit bits
for ( uint8_t i = 0 ; i < 32 ; i + + ) {
nec_pulse ( 21 , true ) ; // bit burst, 560 us
if ( code & 0x1 ) {
nec_pulse ( 61 , false ) ; // bit space, 2.25 ms - 560 us
} else {
nec_pulse ( 21 , false ) ; // bit space, 1.12 ms - 560 us
}
code > > = 1 ; // go to next bit
}
nec_pulse ( 22 , true ) ; // end pulse, 560 us
rim ( ) ; // re-enable interrupts
}
void main ( void )
{
bool master = false ; // if we are not a slave badge, but master controller
sim ( ) ; // disable interrupts (while we reconfigure them)
CLK - > CKDIVR . fields . HSIDIV = CLK_CKDIVR_HSIDIV_DIV0 ; // don't divide internal 16 MHz clock
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
// only power used peripherals
CLK_PCKENR1 = CLK_PCKENR1_UART1234 | CLK_PCKENR1_TIM1 | CLK_PCKENR1_TIM25 | CLK_PCKENR1_TIM46 ;
CLK_PCKENR2 = 0 ;
// 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
UART1 - > CR2 . fields . REN = 1 ; // enable RX
UART1 - > CR2 . fields . RIEN = 1 ; // enable RX interrupt
char uart_cmd [ 10 ] ; // buffer for received data
uint8_t uart_used = 0 ; // how much of the buffer is used
// configure IR demodulator pin
IRM_ON_PORT - > ODR . reg & = ~ IRM_ON_PIN ; // switch IR demodulator off
IRM_ON_PORT - > CR1 . reg | = IRM_ON_PIN ; // use as push-pull
IRM_ON_PORT - > DDR . reg | = IRM_ON_PIN ; // switch pin to output
/* use PWM instead of GPIO for controlling RGB LED
LED_RED_PORT - > ODR . reg & = ~ LED_RED_PIN ; // switch LED off
LED_RED_PORT - > CR1 . reg | = LED_RED_PIN ; // use as push-pull
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 push-pull
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 push-pull
LED_BLUE_PORT - > DDR . reg | = LED_BLUE_PIN ; // use pin to output
*/
// configure timer 2 for PWM-controlling RGB LED
TIM2 - > PSCR . fields . PSC = 0 ; // set prescaler to to 244 Hz, 16E6/(2**0)/65536 = 244 Hz
TIM2 - > ARRH . reg = 0xff ; // set period to max for most precisions
TIM2 - > ARRL . reg = 0xff ; // set period to max for most precisions
TIM2 - > CCMR1 . output_fields . OC1M = 6 ; // set PWM1 mode
TIM2 - > CCMR1 . output_fields . CC1S = 0 ; // use channel as output
TIM2 - > CCER1 . fields . CC1E = 1 ; // enable channel output
led_blue ( 0 ) ; // switch off blue LED
TIM2 - > CCMR2 . output_fields . OC2M = 6 ; // set PWM1 mode
TIM2 - > CCMR2 . output_fields . CC2S = 0 ; // use channel as output
TIM2 - > CCER1 . fields . CC2E = 1 ; // enable channel output
led_red ( 0 ) ; // switch off red LED
TIM2 - > CCMR3 . output_fields . OC3M = 6 ; // set PWM1 mode
TIM2 - > CCMR3 . output_fields . CC3S = 0 ; // use channel as output
TIM2 - > CCER2 . fields . CC3E = 1 ; // enable channel output
led_green ( 0 ) ; // switch off green LED
TIM2 - > EGR . fields . UG = 1 ; // transfer all registers
TIM2 - > CR1 . fields . CEN = 1 ; // enable counter to start PWM
// load color
uint8_t rgb [ 3 ] ; // eyes color
rgb [ 0 ] = * ( uint8_t * ) ( EEPROM_ADDR + 0 ) ; // load red color
rgb [ 1 ] = * ( uint8_t * ) ( EEPROM_ADDR + 1 ) ; // load green color
rgb [ 2 ] = * ( uint8_t * ) ( EEPROM_ADDR + 2 ) ; // load blue color
led_rgb ( rgb ) ; // set color
// 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 update 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 receive IR message
timer_ir_in ( ) ;
/* 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
bool action = false ; // if an action has been performed
puts ( " \r \n ready \r \n " ) ;
while ( true ) {
IWDG_KR = IWDG_KR_KEY_REFRESH ; // reset watchdog
if ( shake_count ) {
puts ( " vibrations: " ) ;
puth ( shake_count ) ;
puts ( " \r \n " ) ;
time_shake = time_count ; // remember time to stay awake
shake_count = 0 ; // reset count
}
if ( nec_flag ) {
puts ( " \r \n r " ) ;
puth ( nec_msg [ 0 ] ) ;
puth ( nec_msg [ 1 ] ) ;
puth ( nec_msg [ 2 ] ) ;
puth ( nec_msg [ 3 ] ) ;
puts ( " \r \n " ) ;
if ( 0x80 = = nec_msg [ 0 ] & & 0x7f = = nec_msg [ 1 ] ) { // radio remote
if ( 0x04 = = nec_msg [ 2 ] & & 0xfb = = nec_msg [ 3 ] ) { // 1
rgb [ 0 ] = 0x80 ;
rgb [ 1 ] = 0 ;
rgb [ 2 ] = 0 ;
} else if ( 0x05 = = nec_msg [ 2 ] & & 0xfa = = nec_msg [ 3 ] ) { // 2
rgb [ 0 ] = 0 ;
rgb [ 1 ] = 0x80 ;
rgb [ 2 ] = 0 ;
} else if ( 0x06 = = nec_msg [ 2 ] & & 0xf9 = = nec_msg [ 3 ] ) { // 3
rgb [ 0 ] = 0 ;
rgb [ 1 ] = 0 ;
rgb [ 2 ] = 0x80 ;
} else if ( 0x01 = = nec_msg [ 2 ] & & 0xfe = = nec_msg [ 3 ] ) { // mute
LED_UV_PORT - > ODR . reg | = LED_UV_PIN ; // switch UV LED on
} else if ( 0x12 = = nec_msg [ 2 ] & & 0xed = = nec_msg [ 3 ] ) { // power
LED_UV_PORT - > ODR . reg & = ~ LED_UV_PIN ; // switch UV LED off
rgb [ 0 ] = 0 ;
rgb [ 1 ] = 0 ;
rgb [ 2 ] = 0 ;
}
led_rgb ( rgb ) ; // ensure [new] color is set
} else if ( 0x01 = = nec_msg [ 0 ] ) { // badge tries to influence us
for ( uint8_t i = 0 ; i < 3 ; i + + ) {
if ( nec_msg [ 1 + i ] > rgb [ i ] ) {
rgb [ i ] + + ;
} else if ( nec_msg [ 1 + i ] < rgb [ i ] ) {
rgb [ i ] - - ;
}
}
led_rgb ( rgb ) ; // set new color
} else if ( 0x02 = = nec_msg [ 0 ] ) { // master sets our color
rgb [ 0 ] = nec_msg [ 1 ] ;
rgb [ 1 ] = nec_msg [ 2 ] ;
rgb [ 2 ] = nec_msg [ 3 ] ;
led_rgb ( rgb ) ; // set new color
}
nec_flag = false ; // clear flag
action = true ; // redo loop
}
if ( ! master & & 0 = = time_count % ( 488UL * SHARE_TIME ) ) {
uint32_t code = 0x01 ; // code to send
code | = ( ( uint32_t ) rgb [ 0 ] < < 8 ) ;
code | = ( ( uint32_t ) rgb [ 1 ] < < 16 ) ;
code | = ( ( uint32_t ) rgb [ 2 ] < < 24 ) ;
nec_transmit ( code ) ;
timer_ir_in ( ) ; // go back to IR capture
putc ( ' t ' ) ;
action = true ; // redo main loop
}
if ( master & & 0 = = time_count % ( 488UL * MASTER_TIME ) ) {
uint32_t code = 0x02 ; // code to send
code | = ( ( uint32_t ) rgb [ 0 ] < < 8 ) ;
code | = ( ( uint32_t ) rgb [ 1 ] < < 16 ) ;
code | = ( ( uint32_t ) rgb [ 2 ] < < 24 ) ;
nec_transmit ( code ) ;
putc ( ' m ' ) ;
action = true ; // redo main loop
}
if ( time_count > time_shake + 488UL * REST_TIME & & ! master ) {
LED_UV_PORT - > ODR . reg & = ~ LED_UV_PIN ; // switch UV LED off
IRM_ON_PORT - > ODR . reg & = ~ IRM_ON_PIN ; // switch IR demodulator off
led_red ( 0 ) ; // ensure LED is off
led_green ( 0 ) ; // ensure LED is off
led_blue ( 0 ) ; // ensure LED is off
// save color
if ( rgb [ 0 ] ! = * ( uint8_t * ) ( EEPROM_ADDR + 0 ) | | rgb [ 1 ] ! = * ( uint8_t * ) ( EEPROM_ADDR + 1 ) | | rgb [ 2 ] ! = * ( uint8_t * ) ( EEPROM_ADDR + 2 ) ) {
// disable DATA (e.g. EEPROM) write protection
if ( 0 = = ( FLASH_IAPSR & FLASH_IAPSR_DUL ) ) {
FLASH_DUKR = FLASH_DUKR_KEY1 ;
FLASH_DUKR = FLASH_DUKR_KEY2 ;
}
for ( uint8_t i = 0 ; i < 3 ; i + + ) {
* ( uint8_t * ) ( EEPROM_ADDR + i ) = rgb [ i ] ;
while ( ! ( FLASH_IAPSR & FLASH_IAPSR_EOP ) ) ; // wait until programming is complete
}
FLASH_IAPSR & = ~ FLASH_IAPSR_DUL ; // re-enable write protection
}
puts ( " rest \r \n \n " ) ;
halt ( ) ;
IRM_ON_PORT - > ODR . reg | = IRM_ON_PIN ; // switch IR demodulator on
led_rgb ( rgb ) ; // set color
time_shake = 0 ; // reset stay awake time
time_count = 0 ; // reset counter
}
if ( uart_c ) { // data received over UART
putc ( uart_c ) ; // echo back
if ( ' \r ' = = uart_c | | ' \n ' = = uart_c ) { // end of line received
if ( uart_used > = 9 & & ' T ' = = uart_cmd [ 0 ] ) { // transmit command
bool code_valid = true ; // verify it it's really a hex string
uint32_t code = 0 ; // parsed code
for ( uint8_t i = 0 ; i < 8 ; i + + ) {
uint32_t n = a2n ( uart_cmd [ 1 + i ] ) ;
if ( n > 0xf ) {
code_valid = false ;
break ;
} else {
code | = ( n < < ( i * 4 ) ) ;
}
}
if ( code_valid ) {
nec_transmit ( code ) ; // transmit code
timer_ir_in ( ) ; // go back to IR capture
puts ( " \r \n code transmitted \r \n " ) ;
}
} else if ( uart_used > = 7 & & ' M ' = = uart_cmd [ 0 ] ) { // switch to master
bool code_valid = true ; // verify it it's really a hex string
uint32_t code = 0x02000000 ; // parsed master code
for ( uint8_t i = 0 ; i < 6 ; i + + ) {
uint32_t n = a2n ( uart_cmd [ 1 + i ] ) ;
if ( n > 0xf ) {
code_valid = false ;
break ;
} else {
code | = ( n < < ( ( i + 1 ) * 4 ) ) ;
}
}
if ( code_valid ) {
rgb [ 0 ] = code > > 8 ; // save color
rgb [ 1 ] = code > > 16 ; // save color
rgb [ 2 ] = code > > 24 ; // save color
led_rgb ( rgb ) ; // set color
nec_transmit ( code ) ; // transmit code
if ( ! master ) {
master = true ; // switch to master mode
IRM_ON_PORT - > ODR . reg & = ~ IRM_ON_PIN ; // switch IR demodulator off
puts ( " \r \n master set \r \n " ) ;
}
}
}
uart_used = 0 ; // reset buffer
} else if ( uart_used < ARRAY_LENGTH ( uart_cmd ) ) {
uart_cmd [ uart_used + + ] = uart_c ;
}
uart_c = 0 ; // clear flag
action = true ; // redo main loop
}
if ( action ) { // something has been performed, check if other flags have been set meanwhile
action = false ; // clear flag
} else { // nothing down
@ -52,8 +581,77 @@ void main(void)
}
}