543 lines
23 KiB
C
543 lines
23 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
|
|
*/
|
|
|
|
/* 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
|
|
#include <libopencm3/stm32/rtc.h> // real time clock 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
|
|
|
|
/** @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 */
|
|
volatile bool photoresistor_flag = false; /**< flag set when ambient luminosity is measured */
|
|
/** @} */
|
|
|
|
/** @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
|
|
* @{
|
|
*/
|
|
/** the number of ticks in one second (depend on the number of LED since it tries to have 255 tick per LED per second) */
|
|
#define TICKS_PER_SECOND 256*WS2812B_LEDS/60
|
|
/** 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 battery_adc ADC used to measure battery voltage
|
|
* @{
|
|
*/
|
|
#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 */
|
|
/** @} */
|
|
/** @defgroup photoresistor_adc ADC used to ambient luminosity
|
|
* @{
|
|
*/
|
|
#define PHOTORESISTOR_ADC_CHANNEL ADC_CHANNEL0 /**< ADC channel */
|
|
#define PHOTORESISTOR_PORT GPIOA /**< port on which the battery is connected */
|
|
#define PHOTORESISTOR_PORT_RCC RCC_GPIOA /**< timer port peripheral clock */
|
|
#define PHOTORESISTOR_PIN GPIO0 /**< 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};
|
|
/** 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};
|
|
/** photo-resistor measurement of ambient luminosity */
|
|
volatile uint16_t photoresistor_value = 0;
|
|
/** photo-resistor voltage for the minimum brightness */
|
|
#define PHOTORESISTOR_MIN 2.7
|
|
/** photo-resistor voltage for the maximum brightness */
|
|
#define PHOTORESISTOR_MAX 1.7
|
|
/** factor to dim LED of the clock, depending on the ambient luminosity */
|
|
float clock_brightness = 1;
|
|
/** minimum LED brightness */
|
|
#define BRIGHTNESS_MIN 0.2
|
|
/** maximum LED brightness */
|
|
#define BRIGHTNESS_MAX 1.0
|
|
/** the factor to change the brightness */
|
|
#define BRIGHTNESS_FACTOR 0.1
|
|
|
|
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;
|
|
}
|
|
|
|
/** @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*(256*(uint64_t)(time%ticks_midday)))/ticks_midday; // scale to LED brightnesses for hours
|
|
uint32_t led_minute = (WS2812B_LEDS*(256*(uint64_t)(time%ticks_hour)))/ticks_hour; // scale to LED brightnesses for minutes
|
|
if (led_hour>=WS2812B_LEDS*256 || led_minute>=WS2812B_LEDS*256) { // 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;
|
|
}
|
|
uint32_t led_second = (WS2812B_LEDS*(256*(uint64_t)(time%ticks_minute)))/ticks_minute; // scale to LED brightnesses for seconds
|
|
uint8_t brightness_second = led_second%256; // get brightness for seconds for last LED
|
|
uint16_t second_led = (WS2812B_LEDS*(time%ticks_minute))/ticks_minute; // get LED for seconds (we only use the last LED as runner instead of all LEDs as arc)
|
|
// set seconds LED
|
|
clock_leds[second_led*3+0] = brightness_second;
|
|
//clock_leds[second_led*3+1] = 0; // clear other colors (minutes/hours indication)
|
|
//clock_leds[second_led*3+2] = 0; // clear other colors (minutes/hours indication)
|
|
// set previous seconds LED
|
|
second_led = ((second_led==0) ? WS2812B_LEDS-1 : second_led-1); // previous LED
|
|
clock_leds[second_led*3+0] = 0xff-brightness_second;
|
|
//clock_leds[second_led*3+1] = 0; // clear other colors (minutes/hours indication)
|
|
//clock_leds[second_led*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)
|
|
{
|
|
for (uint16_t i=0; i<LENGTH(clock_leds)/3; i++) {
|
|
ws2812b_set_rgb(i,gamma_correction_lut[(uint8_t)(clock_leds[i*3+0]*clock_brightness)],gamma_correction_lut[(uint8_t)(clock_leds[i*3+1]*clock_brightness)],gamma_correction_lut[(uint8_t)(clock_leds[i*3+2]*clock_brightness)]); // 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("time [HH:MM:SS]\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 {
|
|
rtc_set_counter_val(((word[0]-'0')*10+(word[1]-'0')*1)*ticks_hour+((word[3]-'0')*10+(word[4]-'0')*1)*ticks_minute+((word[6]-'0')*10+(word[7]-'0')*1)*ticks_second); // set time in RTC counter
|
|
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
|
|
|
|
// setup RTC
|
|
rtc_auto_awake(RCC_LSE, 32768/TICKS_PER_SECOND-1); // ensure RTC is on, uses the 32.678 kHz LSE, and the prescale is set to our tick speed, else update backup registers accordingly
|
|
rtc_interrupt_enable(RTC_SEC); // enable RTC interrupt on "seconds"
|
|
nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt
|
|
|
|
// 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 ADC to read battery voltage
|
|
rcc_periph_clock_enable(BATTERY_PORT_RCC); // enable clock for battery GPIO peripheral
|
|
gpio_set_mode(BATTERY_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, BATTERY_PIN); // set battery GPIO as analogue input for the ADC
|
|
rcc_periph_clock_enable(PHOTORESISTOR_PORT_RCC); // enable clock for photo-resistor GPIO peripheral
|
|
gpio_set_mode(PHOTORESISTOR_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, PHOTORESISTOR_PIN); // set photo-resistor GPIO as analogue input for the ADC
|
|
rcc_periph_clock_enable(RCC_ADC1); // enable clock for ADC peripheral
|
|
adc_off(ADC1); // switch off ADC while configuring it
|
|
// configuration is correct per default
|
|
adc_set_single_conversion_mode(ADC1); // we just want one measurement
|
|
adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC); // use 28.5 cycles to sample (long enough to be stable)
|
|
adc_enable_temperature_sensor(ADC1); // enable internal voltage reference
|
|
adc_enable_discontinuous_mode_regular(ADC1, 1); // do only one conversion per sequence
|
|
adc_enable_external_trigger_regular(ADC1, ADC_CR2_EXTSEL_SWSTART); // use software trigger to start conversion
|
|
adc_power_on(ADC1); // switch on ADC
|
|
for (uint32_t i=0; i<800000; i++) { // wait t_stab for the ADC to stabilize
|
|
__asm__("nop");
|
|
}
|
|
adc_reset_calibration(ADC1); // remove previous non-calibration
|
|
adc_calibration(ADC1); // 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
|
|
|
|
// read internal reference 1.2V and RTC battery voltages
|
|
uint8_t channels[] = {ADC_CHANNEL17, BATTERY_ADC_CHANNEL}; // voltages to convert
|
|
adc_set_regular_sequence(ADC1, LENGTH(channels), channels); // set channels to convert
|
|
adc_start_conversion_regular(ADC1); // start conversion to get first voltage of this group
|
|
while (!adc_eoc(ADC1)); // wait until conversion finished
|
|
uint16_t ref_value = adc_read_regular(ADC1); // read internal reference 1.2V voltage value
|
|
adc_start_conversion_regular(ADC1); // start conversion to get second voltage of this group
|
|
while (!adc_eoc(ADC1)); // wait until conversion finished
|
|
uint16_t battery_value = adc_read_regular(ADC1); // 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);
|
|
|
|
// show voltage on LEDs
|
|
if (battery_voltage<1.0) { // battery probable not connected
|
|
} else if (battery_voltage<2.4) { // low battery voltage
|
|
for (uint16_t led=0; led<2; led++) { // display red on 2 LEDs
|
|
clock_leds[led*3+0] = 0xff; // set red
|
|
}
|
|
} else if (battery_voltage>3.0) { // battery full
|
|
for (uint16_t led=0; led<WS2812B_LEDS; led++) { // display green on all LEDs
|
|
clock_leds[led*3+1] = 0xff; // set green
|
|
}
|
|
} else { // intermediate batter voltage
|
|
for (uint16_t led=0; led<(uint8_t)(WS2812B_LEDS*(battery_voltage-2.4)/0.6); led++) { // display blue on proportional LEDs
|
|
clock_leds[led*3+2] = 0xff; // set blue
|
|
}
|
|
}
|
|
clock_leds_set(); // set the colors of all LEDs
|
|
ws2812b_transmit(); // transmit set color
|
|
for (uint32_t i=0; i<10000000; i++) { // display for a small while
|
|
__asm__("nop");
|
|
}
|
|
|
|
// now use interrupts to only measure ambient luminosity
|
|
channels[0] = PHOTORESISTOR_ADC_CHANNEL; // only measure ambient luminosity
|
|
adc_set_regular_sequence(ADC1, 1, channels); // set now group
|
|
adc_enable_eoc_interrupt(ADC1); // enable interrupt for end of convertion
|
|
nvic_enable_irq(NVIC_ADC1_2_IRQ); // enable ADC interrupts
|
|
|
|
// get date and time
|
|
printf("current time: %02lu:%02lu:%02lu\n", rtc_get_counter_val()/ticks_hour, (rtc_get_counter_val()%ticks_hour)/ticks_minute, (rtc_get_counter_val()%ticks_minute)/ticks_second); // display time
|
|
clock_animate_time(rtc_get_counter_val()); // 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 ((rtc_get_counter_val()%(ticks_second/10))==0) { // one tenth of a second passed
|
|
adc_start_conversion_regular(ADC1); // start measuring ambient luminosity
|
|
}
|
|
if ((rtc_get_counter_val()%ticks_second)==0) { // one second passed
|
|
led_toggle(); // LED activity to show we are not stuck
|
|
}
|
|
if ((rtc_get_counter_val()%ticks_minute)==0) { // one minute passed
|
|
printf("%02lu:%02lu:%02lu\n", rtc_get_counter_val()/ticks_hour, (rtc_get_counter_val()%ticks_hour)/ticks_minute, (rtc_get_counter_val()%ticks_minute)/ticks_second); // display time
|
|
}
|
|
if ((rtc_get_counter_val()%ticks_hour)==0) { // one hours passed
|
|
clock_hours(); // show hour markers
|
|
}
|
|
if (rtc_get_counter_val()>=ticks_midday*2) { // one day passed
|
|
rtc_set_counter_val(rtc_get_counter_val()%ticks_midday); // reset time counter
|
|
}
|
|
clock_set_time(rtc_get_counter_val()); // set time
|
|
}
|
|
while (photoresistor_flag) { // new photo-resistor value has been measured
|
|
photoresistor_flag = false; // reset flag
|
|
action = true; // action has been performed
|
|
float photoresistor_voltage = photoresistor_value*1.2/ref_value; // calculate voltage from value
|
|
float new_clock_brightness = 0; // to calculate new brightness
|
|
if (photoresistor_voltage<PHOTORESISTOR_MAX) { // high ambient luminosity
|
|
new_clock_brightness = BRIGHTNESS_MAX; // set highest brightness
|
|
} else if (photoresistor_voltage>PHOTORESISTOR_MIN) { // low ambient luminosity
|
|
new_clock_brightness = BRIGHTNESS_MIN; // set low brightness
|
|
} else { // intermediate ambient luminosity
|
|
new_clock_brightness = BRIGHTNESS_MIN+(BRIGHTNESS_MAX-BRIGHTNESS_MIN)*(1-(photoresistor_voltage-PHOTORESISTOR_MAX)/(PHOTORESISTOR_MIN-PHOTORESISTOR_MAX)); // set variable brightness
|
|
}
|
|
clock_brightness = clock_brightness*(1-BRIGHTNESS_FACTOR)+new_clock_brightness*BRIGHTNESS_FACTOR; // calculate new brightness based on factor
|
|
//printf("photo-resistor voltage: %f, clock brightness: %f\n", photoresistor_voltage, clock_brightness);
|
|
}
|
|
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)
|
|
/** @brief interrupt service routine called when button is pressed of released */
|
|
void BUTTON_ISR(void)
|
|
{
|
|
exti_reset_request(BUTTON_EXTI); // reset interrupt
|
|
button_flag = true; // perform button action
|
|
}
|
|
#endif
|
|
|
|
/** @brief interrupt service routine called when tick passed on RTC */
|
|
void rtc_isr(void)
|
|
{
|
|
rtc_clear_flag(RTC_SEC); // clear flag
|
|
time_flag = true; // notify to show new time
|
|
}
|
|
|
|
/** @brief interrupt service routine called when ADC conversion completed */
|
|
void adc1_2_isr(void)
|
|
{
|
|
photoresistor_value = adc_read_regular(ADC1); // read measured photo-resistor value (clears interrupt flag)
|
|
photoresistor_flag = true; // notify new ambient luminosity has been measured
|
|
}
|