s8-bathlight/src/main.c

340 lines
6.8 KiB
C

#include <type.h>
#include <stm_base.h>
#include <stm8s103f3.h>
#include <string.h>
#include <stdio.h>
// Use watchdog
#define WATCHDOG
// UART buffer size
#define BUFFER_LEN 48
// LED pin info
#define LED_PIN_DIR PORT_C.DDR.Pin3
#define LED_PIN_CR1 PORT_C.CR1.Pin3
#define LED_PIN_CR2 PORT_C.CR2.Pin3
#define LED_PIN_OUT PORT_C.ODR.Pin3
// UART RX pin info
#define RX_PIN_DIR PORT_D.DDR.Pin6
#define RX_PIN_CR1 PORT_D.CR1.Pin6
#define RX_PIN_CR2 PORT_D.CR2.Pin6
#define RX_PIN_OUT PORT_D.ODR.Pin6
// UART TX pin info
#define TX_PIN_DIR PORT_D.DDR.Pin5
#define TX_PIN_CR1 PORT_D.CR1.Pin5
#define TX_PIN_CR2 PORT_D.CR2.Pin5
#define TX_PIN_OUT PORT_D.ODR.Pin5
// Time defines
#define SHORT_PRESS_MS 25
#define LONG_PRESS_MS 1000
#define LED_FADE_TIME 888
// Countdown timestamp (2^15 max, sign used to notify "unitialized")
volatile i16 countdown_tick = 0;
// Light timer
volatile i16 timer = 0;
// volatile u08 exec = 0;
volatile u08 cmd[BUFFER_LEN];
volatile u08 cmdPos = 0;
volatile u08 cmdLen = 0;
volatile u08 state = 0;
u08 led_state = 0;
/**
* Hang processor with big delay and hope for watchdog to kick-in or directly
* start watchdog restart by setting counter to 0.
*/
void reboot(void) {
#ifdef WATCHDOG
IWATCHDOG.Key = IndependWatchdogReset;
#else
delay_tick(0xffff);
#endif
WWDG.Control.Activate = 1;
WWDG.Control.Counter = 0;
}
/**
* Start feeding cmd into UART (will process with interrupts)
*/
void start_cmd(u08 len) {
cmdPos = 0;
cmdLen = len;
UART1.CR2 |= UART_CR2_TXE_IVT;
UART1.Data = cmd[0];
}
/**
* Mark current time for further comparison
*/
inline void start_countdown(void) {
countdown_tick = (TIM1.CounterH << 8) | TIM1.CounterL;
}
/**
* Mark countdown as unitialized
*/
inline void stop_countdown(void) {
countdown_tick = -1;
}
/**
* Check if countdown is active
*/
inline bool is_countdown_active(void) {
return countdown_tick >= 0;
}
/**
* Get time elapsed from countdown start in ms
*/
i16 elapsed(void) {
if (!is_countdown_active()) {
return -1;
}
u16 next = (TIM1.CounterH << 8) | TIM1.CounterL;
if (next < countdown_tick) {
// Handle case if timer overflowed
return (next + 2500) - countdown_tick;
} else {
return next - countdown_tick;
}
}
void send_some_command(void) {
cmd[0] = 'C';
cmd[1] = 'M';
cmd[2] = 'D';
start_cmd(3);
}
void initialize_state(void) {
switch (state) {
case 0:
send_some_command();
// initialization step 0
break;
case 1:
send_some_command();
// initialization step 1
break;
}
state++;
}
/**
* Fade in LED for ticks defined in LED_FADE_TIME
* Note: if already turned on - do nothing
*/
void turn_on(void) {
if (led_state == 1) {
return;
}
led_state = 1;
u16 delay = LED_FADE_TIME;
while (delay > 1) {
LED_PIN_OUT ^= 1;
delay_tick(delay);
delay--;
}
LED_PIN_OUT = 1;
}
/**
* Fade out LED for ticks defined in LED_FADE_TIME
* Note: if already turned of - do nothing
*/
void turn_off(void) {
if (led_state == 0) {
return;
}
led_state = 0;
u16 delay = 1;
while (delay < LED_FADE_TIME) {
LED_PIN_OUT ^= 1;
delay_tick(delay);
delay++;
}
LED_PIN_OUT = 0;
}
/**
* Tick event handler
*/
void tick_250ms(void) {
// Go trough all initialization states
if (state < 2) {
initialize_state();
// and skip other tasks if not finished
return;
}
// Decrease light timer
if (timer > 0) {
timer--;
}
// If we still have time
// note: timer can be below zero to work without timer
if (timer != 0) {
// Keep light on (will do nothing if already enabled)
turn_on();
} else {
// Turn off otherwise
turn_off();
}
}
interrupt(IRQ_UART1_RX_F, uart_recv) {
u08 s = UART1.Status;
u08 c = UART1.Data;
// if (exec == 1) {
// switch (c) {
// case 0x2A:
// timer = 1200;
// break;
// case 0x2B:
// timer = -1;
// break;
// case 0x2C:
// timer = 0;
// break;
// }
// }
// if (c == 0x24) {
// exec = 1;
// } else {
// exec = 0;
// }
}
interrupt(IRQ_UART1_TX_C, uart_sent) {
cmdPos++;
if (cmdPos < cmdLen) {
UART1.Data = cmd[cmdPos];
} else {
cmdPos = 0;
cmdLen = 0;
memset(cmd, '\0', BUFFER_LEN);
UART1.CR2 &= ~UART_CR2_TXE_IVT;
}
}
interrupt(IRQ_EXTI_C, touch) {
if (PORT_C.IDR.Pin4) {
// Mark touch start
start_countdown();
} else if (is_countdown_active()) {
// Get time on release
i16 time = elapsed();
if (time > LONG_PRESS_MS) {
// Make light continuously if pressed for long
timer = -1;
} else if (time > SHORT_PRESS_MS) {
// If it was short touch set timer for enough time
timer = 30 * 4; // 30 seconds * 4 (250ms tick)
}
}
}
interrupt(IRQ_TIM1_OVERFLOW, timer1_tick) {
TIM1.Status1.UpdateInterruptFlag = 0;
#ifdef WATCHDOG
IWATCHDOG.Key = IndependWatchdogReset;
#endif
tick_250ms();
}
void main(void) {
// Setup clock (External, 16Mhz)
CLK.External.HighSpeedExternalEnable = 1;
while (CLK.External.HighSpeedExternalReady == 0) {
nop();
}
CLK.MasterSwitch = HighSpeedExternal;
while (CLK.SwitchControl.SwitchInterruptFlag == 0) {
nop();
}
CLK.SwitchControl.SwitchStartStop = 1;
CLK.Divider.Prescaler = 0x00;
CLK.Divider.HighSpeedPrescaler = 0x00;
// Disable unused peripherals
CLK.Peripheral1.I2C= 0;
CLK.Peripheral1.SPI = 0;
CLK.Peripheral1.Timer2 = 0;
CLK.Peripheral1.Timer4 = 0;
CLK.Peripheral2.AnalogDigitalConvertor = 0;
// Setup UART pins
RX_PIN_DIR = 0;
RX_PIN_CR1 = 0;
RX_PIN_CR2 = 0;
TX_PIN_DIR = 1;
// Setup touch sensor pins
PORT_C.DDR.Pin4 = 0;
PORT_C.CR1.Pin4 = 0;
PORT_C.CR2.Pin4 = 1;
EXTI_CR1.PortC = EXTI_RISING_FALLING;
// Setup UART (115200 8N1, with interupt)
// 115200 | 9600 | 57600
UART1.CR3 &= ~(UART_CR3_STOP1 | UART_CR3_STOP2);
// UART1.BRR2 = 0x0B; // 0x03; // 0x11;
// UART1.BRR1 = 0x08; // 0x68; // 0x06;
UART1.BRR2 = 0x03;
UART1.BRR1 = 0x68;
UART1.CR2 = UART_CR2_TEN | UART_CR2_REN | UART_CR2_RXNE_IVT | UART_CR2_SBK;
CLK.Peripheral1.Serial1 = 1;
// Setup LED pin
LED_PIN_DIR = 1;
LED_PIN_CR1 = 1;
LED_PIN_CR2 = 0;
LED_PIN_OUT = 1;
// Setup timer 250ms tick
CLK.Peripheral1.Timer1 = 1;
const u16 div = 1599;
const u16 cou = 2500;
TIM1.PrescalerH = div >> 8;
TIM1.PrescalerL = div & 0xFF;
TIM1.AutoReloadH = cou >> 8;
TIM1.AutoReloadL = cou & 0xFF;
TIM1.Interrupt.UpdateInterrupt = 1;
TIM1.Control1.CounterEnable = 1;
#ifdef INVERSION
LED_PIN_OUT = 1;
#else
LED_PIN_OUT = 0;
#endif
#ifdef WATCHDOG
// Start watchdog
IWATCHDOG.Key = IndependWatchdogOn;
IWATCHDOG.Key = IndependWatchdogEdit;
IWATCHDOG.Prescaler = WatchdogPrescaler_256; // Around 128kHz/256=500Hz=2ms
IWATCHDOG.Reload = 0xFF; // 2ms*256=512ms
IWATCHDOG.Key = IndependWatchdogReset;
#endif
// Start interrupts
rim();
// Just for test
CFG_GCR |= 0x02; // Set AL for disable main();
wfi();
// Infinite worker loop
for (;;) {
delay_tick(0xff);
#ifdef WATCHDOG
IWATCHDOG.Key = IndependWatchdogReset;
#endif
}
}