led_clock-old/main.c

575 lines
24 KiB
C

/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** @file main.c
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016
* @brief show the time on a LED strip
*
* The LED strip consists of 60 WS2812b LEDs.
* The time is read from a DS1307 RTC module.
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdio.h> // standard I/O facilities
#include <stdlib.h> // standard utilities
#include <unistd.h> // standard streams
#include <errno.h> // error number utilities
#include <string.h> // string utilities
#include <math.h> // mathematical utilities
/* STM32 (including CM3) libraries */
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/cm3/scb.h> // vector table definition
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
#include <libopencm3/cm3/nvic.h> // interrupt utilities
#include <libopencm3/stm32/exti.h> // external interrupt utilities
#include <libopencm3/stm32/timer.h> // timer utilities
#include <libopencm3/stm32/adc.h> // ADC utilities
/* own libraries */
#include "global.h" // board definitions
#include "usart.h" // USART utilities
#include "usb_cdcacm.h" // USB CDC ACM utilities
#include "led_ws2812b.h" // WS2812b LEDs utilities
#include "rtc_ds1307.h" // Real Time Clock DS1307 utilities
/** @defgroup main_flags flag set in interrupts to be processed in main task
* @{
*/
volatile bool button_flag = false; /**< flag set when board user button has been pressed/released */
volatile bool time_flag = false; /**< flag set when time changed */
/** @} */
#define TICKS_PER_SECOND 256 /**< the number of ticks in one second */
/** @defgroup main_ticks ticks per time units
* @note these are derived from TICKS_PER_SECOND
* @note I have to use type variables because defines would be stored in signed integers, leading to an overflow it later calculations
* @{
*/
/** number of ticks in one second */
const uint32_t ticks_second = TICKS_PER_SECOND;
/** number of ticks in one minute */
const uint32_t ticks_minute = 60*TICKS_PER_SECOND;
/** number of ticks in one hour */
const uint32_t ticks_hour = 60*60*TICKS_PER_SECOND;
/** number of ticks in one midday (12 hours) */
const uint32_t ticks_midday = 12*60*60*TICKS_PER_SECOND;
/** @} */
/** @defgroup square_wave_timer timer peripheral used to count timer based on RTC IC square wave output
* @{
*/
#define SQUARE_WAVE_FREQUENCY 4096 /**< square wave output frequency from the RTC IC */
#define SQUARE_WAVE_TIMER TIM2 /**< timer peripheral */
#define SQUARE_WAVE_TIMER_RCC RCC_TIM2 /**< timer peripheral clock */
#define SQUARE_WAVE_TIMER_IC TIM_IC1 /**< input capture channel (for TIM2_CH1) */
#define SQUARE_WAVE_TIMER_IN TIM_IC_IN_TI1 /**< input capture input source (TIM2_CH1 becomes TI1, then TI1F, then TI1FP1) */
#define SQUARE_WAVE_TIMER_TS TIM_SMCR_TS_IT1FP1 /**< input capture trigger (actually TI1FP1) */
#define SQUARE_WAVE_TIMER_IRQ NVIC_TIM2_IRQ /**< timer interrupt */
#define SQUARE_WAVE_TIMER_ISR tim2_isr /**< timer interrupt service routine */
#define SQUARE_WAVE_GPIO_RCC RCC_GPIOA /**< timer port peripheral clock (TIM2_CH1 on PA0)*/
#define SQUARE_WAVE_GPIO_PORT GPIOA /**< timer port (TIM2_CH1 on PA0) */
#define SQUARE_WAVE_GPIO_PIN GPIO_TIM2_CH1_ETR /**< timer pin input, connect to RTC IC square wave output (TIM2_CH1 on PA0) */
/** @} */
/** @defgroup battery_adc ADC used to measure battery voltage
* @{
*/
#define BATTERY_ADC ADC1 /**< ADC used to measure battery voltage */
#define BATTERY_ADC_RCC RCC_ADC1 /**< ADC clock peripheral */
#define BATTERY_ADC_CHANNEL ADC_CHANNEL1 /**< ADC channel */
#define BATTERY_PORT GPIOA /**< port on which the battery is connected */
#define BATTERY_PORT_RCC RCC_GPIOA /**< timer port peripheral clock */
#define BATTERY_PIN GPIO1 /**< pin of the port on which the battery is connected */
/** @} */
/** RGB values for the WS2812b clock LEDs */
uint8_t clock_leds[WS2812B_LEDS*3] = {0};
/** current time in tick */
volatile uint32_t current_time = 0;
/** user input command */
char command[32] = {0};
/** user input command index */
uint8_t command_i = 0;
/** gamma correction lookup table (common for all colors) */
uint8_t gamma_correction_lut[256] = {0};
int _write(int file, char *ptr, int len)
{
int i;
if (file == STDOUT_FILENO || file == STDERR_FILENO) {
for (i = 0; i < len; i++) {
if (ptr[i] == '\n') { // add carrier return before line feed. this is recommended for most UART terminals
usart_putchar_nonblocking('\r'); // a second line feed doesn't break the display
cdcacm_putchar('\r'); // a second line feed doesn't break the display
}
usart_putchar_nonblocking(ptr[i]); // send byte over USART
cdcacm_putchar(ptr[i]); // send byte over USB
}
return i;
}
errno = EIO;
return -1;
}
void led_on(void)
{
#if defined(SYSTEM_BOARD) || defined(BLUE_PILL)
gpio_clear(LED_PORT, LED_PIN);
#elif defined(MAPLE_MINI)
gpio_set(LED_PORT, LED_PIN);
#endif
}
void led_off(void)
{
#if defined(SYSTEM_BOARD) || defined(BLUE_PILL)
gpio_set(LED_PORT, LED_PIN);
#elif defined(MAPLE_MINI)
gpio_clear(LED_PORT, LED_PIN);
#endif
}
void led_toggle(void)
{
gpio_toggle(LED_PORT, LED_PIN);
}
/** @brief switch off all clock LEDs
* @note LEDs need to be set separately
*/
static void clock_clear(void)
{
// set all colors of all LEDs to 0
for (uint16_t i=0; i<LENGTH(clock_leds); i++) {
clock_leds[i] = 0;
}
}
/** @brief show time on LED clock
* @param[in] time in ticks to show
* @details show hours and minutes progress as full arcs, show second position as marker. the brightness of the LED shows the progress of the unit. hours are blue, minutes green, seconds red
* @note LEDs need to be set separately
*/
static void clock_show_time(uint32_t time)
{
uint32_t led_hour = (WS2812B_LEDS*(255*(uint64_t)(time%ticks_midday)))/ticks_midday; // scale to LED brightnesses for hours
uint32_t led_minute = (WS2812B_LEDS*(255*(uint64_t)(time%ticks_hour)))/ticks_hour; // scale to LED brightnesses for minutes
if (led_hour>=WS2812B_LEDS*255 || led_minute>=WS2812B_LEDS*255) { // a calculation error occurred
return;
}
// show hours and minutes on LEDs
if (led_hour>led_minute) {
// show hours in blue (and clear other LEDs)
for (uint16_t led=0; led<WS2812B_LEDS; led++) {
clock_leds[led*3+0] = 0;
clock_leds[led*3+1] = 0;
if (led_hour>=0xff) { // full hours
clock_leds[led*3+2] = 0xff;
} else { // running hours
clock_leds[led*3+2] = led_hour;
}
led_hour -= clock_leds[led*3+2];
}
// show minutes in green (override hours)
for (uint16_t led=0; led<WS2812B_LEDS && led_minute>0; led++) {
clock_leds[led*3+0] = 0;
if (led_minute>=0xff) { // full minutes
clock_leds[led*3+1] = 0xff;
} else { // running minutes
clock_leds[led*3+1] = led_minute;
}
led_minute -= clock_leds[led*3+1];
clock_leds[led*3+2] = 0;
}
} else {
// show minutes in green (and clear other LEDs)
for (uint16_t led=0; led<WS2812B_LEDS; led++) {
clock_leds[led*3+0] = 0;
if (led_minute>=0xff) { // full minutes
clock_leds[led*3+1] = 0xff;
} else { // running minutes
clock_leds[led*3+1] = led_minute;
}
led_minute -= clock_leds[led*3+1];
clock_leds[led*3+2] = 0;
}
// show hours in blue (override minutes)
for (uint16_t led=0; led<WS2812B_LEDS && led_hour>0; led++) {
clock_leds[led*3+0] = 0;
clock_leds[led*3+1] = 0;
if (led_hour>=0xff) { // full hours
clock_leds[led*3+2] = 0xff;
} else { // running hours
clock_leds[led*3+2] = led_hour;
}
led_hour -= clock_leds[led*3+2];
}
}
// don't show seconds on full minute (better for first time setting, barely visible else)
if (time%ticks_minute==0) {
return;
}
uint16_t led_second = (WS2812B_LEDS*(time%ticks_minute))/ticks_minute; // get LED for seconds
uint8_t brightness_second = (255*(time%ticks_second))/ticks_second; // get brightness for seconds
// set seconds LED
clock_leds[led_second*3+0] = brightness_second;
//clock_leds[led_second*3+1] = 0; // clear other colors (minutes/hours indication)
//clock_leds[led_second*3+2] = 0; // clear other colors (minutes/hours indication)
// set previous seconds LED
led_second = ((led_second==0) ? WS2812B_LEDS-1 : led_second-1); // previous LED
clock_leds[led_second*3+0] = 0xff-brightness_second;
//clock_leds[led_second*3+1] = 0; // clear other colors (minutes/hours indication)
//clock_leds[led_second*3+2] = 0; // clear other colors (minutes/hours indication)
}
/** @brief set the LEDs
* @details set the LED colors on WS2812b LEDs
* @note WS2812b LED color values need to be transmitted separately
*/
static void clock_leds_set(void)
{
static uint8_t clock_leds_old[WS2812B_LEDS*3] = {0}; // backup so to only set when value changed
for (uint16_t i=0; i<LENGTH(clock_leds)/3; i++) {
if (clock_leds[i*3+0]!=clock_leds_old[i*3+0] || clock_leds[i*3+1]!=clock_leds_old[i*3+1] || clock_leds[i*3+2]!=clock_leds_old[i*3+2]) { // only set when value changed (since this costs time)
clock_leds_old[i*3+0] = clock_leds[i*3+0]; // memorise change
clock_leds_old[i*3+1] = clock_leds[i*3+1]; // memorise change
clock_leds_old[i*3+2] = clock_leds[i*3+2]; // memorise change
ws2812b_set_rgb(i,gamma_correction_lut[clock_leds[i*3+0]],gamma_correction_lut[clock_leds[i*3+1]],gamma_correction_lut[clock_leds[i*3+2]]); // set new value (this costs time)
}
}
}
/** @brief set the time on the LEDs
* @param[in] time time to set
*/
static void clock_set_time(uint32_t time)
{
clock_show_time(time); // set time
clock_leds_set(); // set the colors of all LEDs
ws2812b_transmit(); // transmit set color
}
/** @brief incrementally set the time on the LEDs
* @details this will have an animation where time is incremented until it reaches the provided time
* @param[in] time time to set
*/
static void clock_animate_time(uint32_t time)
{
static uint32_t display_time = 0; // the time to display
while (display_time<time) {
if (display_time+ticks_hour<=time) { // first set hours
display_time += ticks_hour; // increment hours
} else if (display_time+ticks_minute<=time) { // second set minutes
display_time += ticks_minute; // increment minutes
} else if (display_time+ticks_second<=time) { // third set seconds
display_time += ticks_second; // increment seconds
} else { // finally set time
display_time = time;
}
clock_set_time(display_time); // set time (progress)
// delay some time for the animation
for (uint32_t i=0; i<400000; i++) {
__asm__("nop");
}
}
}
/** @brief show animation with fading hours mark on clock LEDs
*/
static void clock_hours(void)
{
for (uint16_t i=0; i<512; i++) { // fade in and out
uint8_t brightness = (i>255 ? 512-i-1 : i); // get fade brightness
for (uint8_t hour=0; hour<12; hour++) { // set all hour colors
uint16_t led = WS2812B_LEDS/12*hour; // get LED four hour mark
clock_leds[led*3+0] = brightness; // set brightness
clock_leds[led*3+1] = brightness; // set brightness
clock_leds[led*3+2] = brightness; // set brightness
}
clock_leds_set(); // set the colors of all LEDs
ws2812b_transmit(); // transmit set color
// delay some time for the animation
for (uint32_t j=0; j<40000; j++) {
__asm__("nop");
}
}
}
/** @brief process user command
* @param[in] str user command string (\0 ended)
*/
static void process_command(char* str)
{
/* split command */
const char* delimiter = " ";
char* word = strtok(str,delimiter);
if (!word) {
goto error;
}
/* parse command */
if (0==strcmp(word,"help")) {
printf("available commands:\n");
printf("date [YYYY-MM-DD]\n");
printf("time [HH:MM:SS]\n");
} else if (0==strcmp(word,"date")) {
word = strtok(NULL,delimiter);
if (!word) {
printf("current date: %04d-%02d-%02d\n", rtc_read_year(), rtc_read_month(), rtc_read_date());
} else if (strlen(word)!=10 || word[0]!='2' || word[1]!='0' || word[2]<'0' || word[2]>'9' || word[3]<'0' || word[3]>'9' || word[5]<'0' || word[5]>'1' || word[6]<'0' || word[6]>'9' || word[8]<'0' || word[8]>'3' || word[9]<'0' || word[9]>'9') {
goto error;
} else {
if (!rtc_write_year((word[0]-'0')*1000+(word[1]-'0')*100+(word[2]-'0')*10+(word[3]-'0')*1)) {
printf("setting year failed\n");
} else if (!rtc_write_month((word[5]-'0')*10+(word[6]-'0')*1)) {
printf("setting month failed\n");
} else if (!rtc_write_date((word[8]-'0')*10+(word[9]-'0')*1)) {
printf("setting day failed\n");
} else {
printf("date set\n");
}
}
} else if (0==strcmp(word,"time")) {
word = strtok(NULL,delimiter);
if (!word) {
printf("current time: %02d:%02d:%02d\n", rtc_read_hours(), rtc_read_minutes(), rtc_read_seconds());
} else if (strlen(word)!=8 || word[0]<'0' || word[0]>'2' || word[1]<'0' || word[1]>'9' || word[3]<'0' || word[3]>'5' || word[4]<'0' || word[4]>'9' || word[6]<'0' || word[6]>'5' || word[7]<'0' || word[7]>'9') {
goto error;
} else {
if (!rtc_write_hours((word[0]-'0')*10+(word[1]-'0')*1)) {
printf("setting hours failed\n");
} else if (!rtc_write_minutes((word[3]-'0')*10+(word[4]-'0')*1)) {
printf("setting minutes failed\n");
} else if (!rtc_write_seconds((word[6]-'0')*10+(word[7]-'0')*1)) {
printf("setting seconds failed\n");
} else {
current_time = rtc_read_hours()*ticks_hour+rtc_read_minutes()*ticks_minute+rtc_read_seconds()*ticks_second; // set also internal time
rtc_oscillator_enable(); // be sure the oscillation is enabled
printf("time set\n");
}
}
} else {
goto error;
}
return; // command successfully processed
error:
puts("command not recognized. enter help to list commands");
}
/** @brief program entry point
* this is the firmware function started by the micro-controller
*/
int main(void)
{
SCB_VTOR = (uint32_t) 0x08002000; // relocate vector table because of the bootloader
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
usart_setup(); // setup USART (for printing)
cdcacm_setup(); // setup USB CDC ACM (for printing)
setbuf(stdout, NULL); // set standard out buffer to NULL to immediately print
setbuf(stderr, NULL); // set standard error buffer to NULL to immediately print
// setup LED
rcc_periph_clock_enable(LED_RCC); // enable clock for LED
gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, LED_PIN); // set LED pin to 'output push-pull'
led_off(); // switch off LED to indicate setup started
// setup button
#if defined(BUTTON_RCC) && defined(BUTTON_PORT) && defined(BUTTON_PIN) && defined(BUTTON_EXTI) && defined(BUTTON_IRQ)
rcc_periph_clock_enable(BUTTON_RCC); // enable clock for button
gpio_set_mode(BUTTON_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, BUTTON_PIN); // set button pin to input
rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
exti_select_source(BUTTON_EXTI, BUTTON_PORT); // mask external interrupt of this pin only for this port
exti_set_trigger(BUTTON_EXTI, EXTI_TRIGGER_BOTH); // trigger on both edge
exti_enable_request(BUTTON_EXTI); // enable external interrupt
nvic_enable_irq(BUTTON_IRQ); // enable interrupt
#endif
// generate gamma correction table (with fixed gamma value)
for (uint16_t i=0; i<LENGTH(gamma_correction_lut); i++) {
gamma_correction_lut[i] = powf((float)i / (float)LENGTH(gamma_correction_lut), 2.2)*LENGTH(gamma_correction_lut);
}
// setup WS2812b LEDs
ws2812b_setup(); // setup WS2812b LEDs
clock_clear(); // clear all LEDs
clock_leds_set(); // set the colors of all LEDs
ws2812b_transmit(); // transmit set color
// setup RTC module
rtc_setup(); // setup RTC module
rtc_write_square_wave(SQUARE_WAVE_FREQUENCY); // set square wave output frequency
// setup timer to generate tick from square wave output */
rcc_periph_clock_enable(SQUARE_WAVE_GPIO_RCC); // enable clock for GPIO peripheral
gpio_set_mode(SQUARE_WAVE_GPIO_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SQUARE_WAVE_GPIO_PIN); // set pin as input
gpio_set(SQUARE_WAVE_GPIO_PORT, SQUARE_WAVE_GPIO_PIN); // enable pull-up
rcc_periph_clock_enable(SQUARE_WAVE_TIMER_RCC); // enable clock for timer peripheral
timer_reset(SQUARE_WAVE_TIMER); // reset timer state
timer_ic_set_input(SQUARE_WAVE_TIMER, SQUARE_WAVE_TIMER_IC, SQUARE_WAVE_TIMER_IN); // configure channel as input capture
timer_ic_set_filter(SQUARE_WAVE_TIMER, SQUARE_WAVE_TIMER_IC, TIM_IC_OFF); // use no input capture filter
timer_ic_set_polarity(SQUARE_WAVE_TIMER, SQUARE_WAVE_TIMER_IC, TIM_IC_FALLING); //capture on falling edge
timer_slave_set_trigger(SQUARE_WAVE_TIMER, SQUARE_WAVE_TIMER_TS); // select trigger
timer_slave_set_mode(SQUARE_WAVE_TIMER, TIM_SMCR_SMS_ECM1); // select external clock more 1 as input
timer_ic_enable(SQUARE_WAVE_TIMER, SQUARE_WAVE_TIMER_IC); // enable input capture
timer_set_mode(SQUARE_WAVE_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
timer_set_prescaler(SQUARE_WAVE_TIMER, 0); // no need to prescale
timer_set_period(SQUARE_WAVE_TIMER, SQUARE_WAVE_FREQUENCY/TICKS_PER_SECOND-1); // set the tick period
timer_enable_irq(SQUARE_WAVE_TIMER, TIM_DIER_UIE); // enable interrupt for timer
nvic_enable_irq(SQUARE_WAVE_TIMER_IRQ); // allow interrupt for timer
timer_enable_counter(SQUARE_WAVE_TIMER); // enable timer to count ticks
// setup ADC to ready battery voltage (single conversion of a regular channel without interrupt)
rcc_periph_clock_enable(BATTERY_PORT_RCC); // enable clock for GPIO peripheral
gpio_set_mode(BATTERY_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, BATTERY_PIN); // set GPIO as analogue input for the ADC
rcc_periph_clock_enable(BATTERY_ADC_RCC); // enable clock for ADC peripheral
adc_off(BATTERY_ADC); // switch off ADC while configuring it
// configuration is correct per default
adc_set_single_conversion_mode(BATTERY_ADC); // we just want one measurement
adc_set_sample_time(BATTERY_ADC, BATTERY_ADC_CHANNEL, ADC_SMPR_SMP_28DOT5CYC); // use 28.5 cycles to sample (long enough to be stable)
adc_enable_temperature_sensor(BATTERY_ADC); // enable internal voltage reference
adc_set_sample_time(BATTERY_ADC, ADC_CHANNEL17, ADC_SMPR_SMP_28DOT5CYC); // use 28.5 cycles to sample internal voltage reference
adc_power_on(BATTERY_ADC); // switch on ADC
for (uint32_t i = 0; i < 800000; i++) { // wait t_stab for the ADC to stabilize
__asm__("nop");
}
adc_reset_calibration(BATTERY_ADC); // remove previous non-calibration
adc_calibration(BATTERY_ADC); // calibrate ADC for less accuracy errors
printf("welcome to the CuVoodoo LED clock\n"); // print welcome message
led_on(); // switch on LED to indicate setup completed
// verify is RTC is running
if (rtc_oscillator_disabled()) {
printf("/!\\ RTC oscillator is disabled\n");
}
// read internal reference 1.2V voltage
uint8_t channels[] = {ADC_CHANNEL17}; // voltages to convert
adc_set_regular_sequence(BATTERY_ADC, LENGTH(channels), channels); // set channels to convert
adc_start_conversion_direct(BATTERY_ADC); // start conversion to get voltages
while (!adc_eoc(BATTERY_ADC)); // wait until conversion finished
uint16_t ref_value = adc_read_regular(BATTERY_ADC); // read internal reference 1.2V voltage value
// check RTC battery voltage
channels[0] = BATTERY_ADC_CHANNEL; // voltage to convert
adc_set_regular_sequence(BATTERY_ADC, LENGTH(channels), channels); // set channels to convert
adc_start_conversion_direct(BATTERY_ADC); // start conversion to get voltages
while (!adc_eoc(BATTERY_ADC)); // wait until conversion finished
uint16_t battery_value = adc_read_regular(BATTERY_ADC); // read converted battery voltage
float battery_voltage = battery_value*1.2/ref_value; // calculate battery voltage
if (battery_voltage<2.4) {
printf("/!\\ low ");
}
printf("battery voltage: %.2fV\n", battery_voltage);
// get date and time
uint16_t* rtc_time = rtc_read_time(); // get RTC time/date
current_time = rtc_time[2]*ticks_hour+rtc_time[1]*ticks_minute+rtc_time[0]*ticks_second; // the current time
printf("current date: %04d-%02d-%02d %02d:%02d:%02d\n", rtc_time[6], rtc_time[5], rtc_time[4], rtc_time[2], rtc_time[1], rtc_time[0]);
clock_animate_time(current_time); // set time with animation
printf("input commands\n");
bool action = false; // if an action has been performed don't go to sleep
button_flag = false; // reset button flag
char c = ' '; // to store received character
bool char_flag = false; // a new character has been received
while (true) { // infinite loop
while (usart_received) { // data received over UART
action = true; // action has been performed
led_toggle(); // toggle LED
c = usart_getchar(); // store receive character
char_flag = true; // notify character has been received
}
while (cdcacm_received) { // data received over USB
action = true; // action has been performed
led_toggle(); // toggle LED
c = usart_getchar(); // store receive character
char_flag = true; // notify character has been received
}
while (char_flag) { // user data received
char_flag = false; // reset flag
action = true; // action has been performed
printf("%c",c); // echo receive character
if (c=='\n') { // end of command received
if (command_i>0) { // there is a command to process
command[command_i] = 0; // end string
command_i = 0; // prepare for next command
process_command(command); // process user command
}
} else if (c!='\r') { // user command input
command[command_i] = c; // save command input
if (command_i<LENGTH(command)-2) { // verify if there is place to save next character
command_i++; // save next character
}
}
}
while (button_flag) { // user pressed button
button_flag = false; // reset flag
action = true; // action has been performed
led_toggle(); // toggle LED
}
while (time_flag) { // time passed
time_flag = false; // reset flag
action = true; // action has been performed
if ((current_time%ticks_second)==0) { // one second passed
//printf("%02lu:%02lu:%02lu\n", current_time/ticks_hour, (current_time%ticks_hour)/ticks_minute, (current_time%ticks_minute)/ticks_second); // display time
}
if ((current_time%ticks_minute)==0) { // one minute passed
rtc_time = rtc_read_time(); // get RTC time/date
current_time = rtc_time[2]*ticks_hour+rtc_time[1]*ticks_minute+rtc_time[0]*ticks_second; // sync current time
printf("it is now %02lu:%02lu:%02lu\n", current_time/ticks_hour, (current_time%ticks_hour)/ticks_minute, (current_time%ticks_minute)/ticks_second); // display time
}
if ((current_time%ticks_hour)==0) { // one hours passed
clock_hours(); // show hour markers
}
clock_set_time(current_time); // set time
if ((current_time%ticks_second)==0) { // on second passed
//printf("%02lu:%02lu:%02lu\n", current_time/ticks_hour, (current_time%ticks_hour)/ticks_minute, (current_time%ticks_minute)/ticks_second); // display time
}
}
if (action) { // go to sleep if nothing had to be done, else recheck for activity
action = false;
} else {
__WFI(); // go to sleep
}
}
return 0;
}
#if defined(BUTTON_ISR) && defined(BUTTON_EXTI)
void BUTTON_ISR(void)
{
exti_reset_request(BUTTON_EXTI); // reset interrupt
button_flag = true; // perform button action
}
#endif
#if defined(SQUARE_WAVE_TIMER_IRQ) && defined(SQUARE_WAVE_TIMER_ISR)
/** @brief timer interrupt when square wave output accumulated to a tick */
void SQUARE_WAVE_TIMER_ISR(void)
{
if (timer_get_flag(SQUARE_WAVE_TIMER, TIM_SR_UIF)) { // overflow even happened
timer_clear_flag(SQUARE_WAVE_TIMER, TIM_SR_UIF); // clear flag
current_time++; // increment time
time_flag = true; // update flag
}
}
#endif