stm32f1/application.c

595 lines
25 KiB
C
Raw Normal View History

2016-01-17 14:54:54 +01:00
/* 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/>.
*
*/
2017-04-19 16:24:07 +02:00
/** STM32F1 application example
2017-08-02 13:46:20 +02:00
* @file application.c
2016-08-14 21:02:38 +02:00
* @author King Kévin <kingkevin@cuvoodoo.info>
2017-04-19 16:24:07 +02:00
* @date 2016-2017
2016-08-14 21:02:38 +02:00
*/
2016-01-17 14:54:54 +01:00
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdlib.h> // standard utilities
2016-08-14 21:02:38 +02:00
#include <string.h> // string utilities
#include <math.h> // mathematical utilities
2016-01-17 14:54:54 +01:00
/* STM32 (including CM3) libraries */
2016-01-18 16:23:35 +01:00
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
2016-10-23 17:42:27 +02:00
#include <libopencm3/cm3/scb.h> // vector table definition
2016-01-29 11:25:30 +01:00
#include <libopencm3/cm3/nvic.h> // interrupt utilities
2016-10-23 17:42:27 +02:00
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/rcc.h> // real-time control clock library
2016-01-29 11:25:30 +01:00
#include <libopencm3/stm32/exti.h> // external interrupt utilities
2016-08-14 21:02:38 +02:00
#include <libopencm3/stm32/rtc.h> // real time clock utilities
2016-10-23 17:42:27 +02:00
#include <libopencm3/stm32/iwdg.h> // independent watchdog utilities
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
#include <libopencm3/stm32/flash.h> // flash utilities
#include <libopencm3/stm32/adc.h> // ADC utilities
#include <libopencm3/stm32/rtc.h> // real time clock utilities
2016-01-17 14:54:54 +01:00
/* own libraries */
#include "global.h" // board definitions
2017-04-03 13:32:45 +02:00
#include "print.h" // printing utilities
2016-01-17 14:54:54 +01:00
#include "usart.h" // USART utilities
2016-01-18 16:23:35 +01:00
#include "usb_cdcacm.h" // USB CDC ACM utilities
#include "led_ws2812b.h" // WS2812B LEDs utilities
#include "rtc_dcf77.h" // DCF77 time receiver utilities
/** use external RTC, else use internal RTC */
#define EXTERNAL_RTC false
2016-01-17 14:54:54 +01:00
2016-10-23 17:42:27 +02:00
#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */
2016-08-14 21:02:38 +02:00
/** @defgroup main_flags flag set in interrupts to be processed in main task
* @{
*/
volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
volatile bool photoresistor_flag = false; /**< flag set when ambient luminosity is measured */
2016-08-14 21:02:38 +02:00
/** @} */
/** @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 (32768 divisor greater than 256*LED_WS2812B_LEDS/60) */
2017-10-08 18:09:52 +02:00
#define TICKS_PER_SECOND (256UL)
/** number of ticks in one second */
2017-10-08 18:09:52 +02:00
#define TICKS_SECOND (TICKS_PER_SECOND)
/** number of ticks in one minute */
2017-10-08 18:09:52 +02:00
#define TICKS_MINUTE (60*TICKS_SECOND)
/** number of ticks in one hour */
2017-10-08 18:09:52 +02:00
#define TICKS_HOUR (60*TICKS_MINUTE)
/** number of ticks in one midday (12 hours) */
2017-10-08 18:09:52 +02:00
#define TICKS_MIDDAY (12*TICKS_HOUR)
/** @} */
/** @defgroup photoresistor_adc ADC used to ambient luminosity
* @{
*/
#define PHOTORESISTOR_ADC_CHANNEL 1 /**< ADC channel */
/** @} */
/** RGB values for the WS2812B clock LEDs */
uint8_t clock_leds[LED_WS2812B_LEDS*3] = {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
2017-04-03 13:32:45 +02:00
size_t putc(char c)
2016-01-17 14:54:54 +01:00
{
2017-04-03 13:32:45 +02:00
size_t length = 0; // number of characters printed
static char newline = 0; // to remember on which character we sent the newline
if (0==c) {
length = 0; // don't print string termination character
} else if ('\r' == c || '\n' == c) { // send CR+LF newline for most carriage return and line feed combination
if (0==newline || c==newline) { // send newline only if not already send (and only once on \r\n or \n\r)
usart_putchar_nonblocking('\r'); // send CR over USART
2017-04-19 16:24:07 +02:00
usb_cdcacm_putchar('\r'); // send CR over USB
2017-04-03 13:32:45 +02:00
usart_putchar_nonblocking('\n'); // send LF over USART
2017-04-19 16:24:07 +02:00
usb_cdcacm_putchar('\n'); // send LF over USB
2017-04-03 13:32:45 +02:00
length += 2; // remember we printed 2 characters
newline = c; // remember on which character we sent the newline
} else {
length = 0; // the \r or \n of \n\r or \r\n has already been printed
2016-01-17 14:54:54 +01:00
}
2017-04-03 13:32:45 +02:00
} else {
usart_putchar_nonblocking(c); // send byte over USART
2017-04-19 16:24:07 +02:00
usb_cdcacm_putchar(c); // send byte over USB
2017-04-03 13:32:45 +02:00
newline = 0; // clear new line
length++; // remember we printed 1 character
2016-01-17 14:54:54 +01:00
}
2017-04-03 13:32:45 +02:00
return length; // return number of characters printed
2016-01-17 14:54:54 +01:00
}
2016-10-23 17:42:27 +02:00
/** user input command */
static char command[32] = {0};
/** user input command index */
uint8_t command_i = 0;
2016-01-17 14:54:54 +01:00
2016-08-14 21:02:38 +02:00
/** process user command
* @param[in] str user command string (\0 ended)
*/
static void process_command(char* str)
2016-01-17 14:54:54 +01:00
{
2016-08-14 21:02:38 +02:00
// split command
const char* delimiter = " ";
char* word = strtok(str,delimiter);
if (!word) {
goto error;
}
// parse command
2017-04-19 16:24:07 +02:00
if (0==strcmp(word,"h") || 0==strcmp(word,"help") || 0==strcmp(word,"?")) {
2016-08-14 21:02:38 +02:00
printf("available commands:\n");
printf("led [on|off|toggle]\n");
printf("time [HH:MM:SS]\n");
printf("DCF77 on|off\n");
2017-04-19 16:24:07 +02:00
} else if (0==strcmp(word,"l") || 0==strcmp(word,"led")) {
2016-08-14 21:02:38 +02:00
word = strtok(NULL,delimiter);
if (!word) {
2017-04-19 16:24:07 +02:00
printf("LED is ");
if (gpio_get(GPIO(LED_PORT), GPIO(LED_PIN))) {
printf("on\n");
} else {
printf("off\n");
}
2016-08-14 21:02:38 +02:00
} else if (0==strcmp(word,"on")) {
led_on(); // switch LED on
printf("LED switched on\n"); // notify user
} else if (0==strcmp(word,"off")) {
led_off(); // switch LED off
printf("LED switched off\n"); // notify user
} else if (0==strcmp(word,"toggle")) {
led_toggle(); // toggle LED
printf("LED toggled\n"); // notify user
} else {
goto error;
}
2017-06-27 15:44:03 +02:00
} else if (0==strcmp(word,"time")) {
word = strtok(NULL,delimiter);
if (!word) {
2017-10-08 18:09:52 +02:00
printf("time: %02U:%02U:%02U\n", rtc_get_counter_val()/TICKS_HOUR, (rtc_get_counter_val()%TICKS_HOUR)/TICKS_MINUTE, (rtc_get_counter_val()%TICKS_MINUTE)/TICKS_SECOND); // get and print time from internal RTC
2017-06-27 15:44:03 +02:00
} 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') { // time format is incorrect
goto error;
} else {
2017-10-08 18:09:52 +02:00
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 internal RTC counter
2017-06-27 15:44:03 +02:00
printf("time set\n");
}
} else if (0==strcmp(word,"DCF77")) {
2017-06-27 15:44:03 +02:00
word = strtok(NULL,delimiter);
if (!word) {
goto error;
} else if (0==strcmp(word,"on")) {
rtc_dcf77_on(); // switch DCF77 on
printf("DCF77 receiver switched on\n"); // notify user
} else if (0==strcmp(word,"off")) {
rtc_dcf77_off(); // switch DCF77 off
printf("DCF77 receiver switched off\n"); // notify user
2017-06-27 15:44:03 +02:00
} else {
goto error;
2017-06-27 15:44:03 +02:00
}
2016-08-14 21:02:38 +02:00
} else {
goto error;
}
return; // command successfully processed
error:
printf("command not recognized. enter help to list commands\n");
2016-10-23 17:42:27 +02:00
return;
2016-01-17 14:54:54 +01:00
}
/** 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;
}
}
/** 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)
{
2017-10-08 18:09:52 +02:00
uint32_t led_hour = (LED_WS2812B_LEDS*256ULL*(time%TICKS_MIDDAY))/TICKS_MIDDAY; // scale to LED brightnesses for hours
uint32_t led_minute = (LED_WS2812B_LEDS*256ULL*(time%TICKS_HOUR))/TICKS_HOUR; // scale to LED brightnesses for minutes
if (led_hour>=LED_WS2812B_LEDS*256 || led_minute>=LED_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<LED_WS2812B_LEDS; led++) {
2017-10-08 18:09:52 +02:00
clock_leds[led*3+0] = 0; // clear red (seconds)
clock_leds[led*3+1] = 0; // clear green (minutes)
if (led_hour>=0xff) { // full hours
2017-10-08 18:09:52 +02:00
clock_leds[led*3+2] = 0xff; // set blue (hours) to full
} else { // running hours
2017-10-08 18:09:52 +02:00
clock_leds[led*3+2] = led_hour; // set blue (hours) to remaining
}
2017-10-08 18:09:52 +02:00
led_hour -= clock_leds[led*3+2]; // subtract displayed value
}
// show minutes in green (override hours)
for (uint16_t led=0; led<LED_WS2812B_LEDS && led_minute>0; led++) {
2017-10-08 18:09:52 +02:00
clock_leds[led*3+0] = 0; // clear red (seconds)
if (led_minute>=0xff) { // full minutes
2017-10-08 18:09:52 +02:00
clock_leds[led*3+1] = 0xff; // set green (minutes) to full
} else { // running minutes
2017-10-08 18:09:52 +02:00
clock_leds[led*3+1] = led_minute; // set green (minutes) to remaining
}
2017-10-08 18:09:52 +02:00
led_minute -= clock_leds[led*3+1]; // subtract displayed value
clock_leds[led*3+2] = 0; // clear blue (hours)
}
} else {
// show minutes in green (and clear other LEDs)
for (uint16_t led=0; led<LED_WS2812B_LEDS; led++) {
2017-10-08 18:09:52 +02:00
clock_leds[led*3+0] = 0; // clear red (seconds)
if (led_minute>=0xff) { // full minutes
2017-10-08 18:09:52 +02:00
clock_leds[led*3+1] = 0xff; // set green (minutes) to full
} else { // running minutes
2017-10-08 18:09:52 +02:00
clock_leds[led*3+1] = led_minute; // set green (minutes) to remaining
}
2017-10-08 18:09:52 +02:00
led_minute -= clock_leds[led*3+1]; // subtract displayed value
clock_leds[led*3+2] = 0; // clear blue (hours)
}
// show hours in blue (override minutes)
for (uint16_t led=0; led<LED_WS2812B_LEDS && led_hour>0; led++) {
2017-10-08 18:09:52 +02:00
clock_leds[led*3+0] = 0; // clear red (seconds)
clock_leds[led*3+1] = 0; // clear green (minutes)
if (led_hour>=0xff) { // full hours
2017-10-08 18:09:52 +02:00
clock_leds[led*3+2] = 0xff; // set blue (hours) to full
} else { // running hours
2017-10-08 18:09:52 +02:00
clock_leds[led*3+2] = led_hour; // set blue (hours) to remaining
}
2017-10-08 18:09:52 +02:00
led_hour -= clock_leds[led*3+2]; // subtract displayed value
}
}
// don't show seconds on full minute (better for first time setting, barely visible else)
2017-10-08 18:09:52 +02:00
if (time%TICKS_MINUTE==0) {
return;
}
2017-10-08 18:09:52 +02:00
uint32_t led_second = (LED_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
2017-10-08 18:09:52 +02:00
uint16_t second_led = (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) ? LED_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)
}
/** 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++) {
led_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)
}
}
/** set the time on the LEDs
* @param[in] time time to set
*/
static void clock_set_time(uint32_t time)
{
2017-10-08 18:09:52 +02:00
clock_show_time(time); // convert time to LED values
clock_leds_set(); // set the colors of all LEDs
led_ws2812b_transmit(); // transmit set color
}
/** 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) {
2017-10-08 18:09:52 +02:00
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");
}
}
}
/** show animation with fading hours mark on clock LEDs
*/
static void clock_hours(void)
{
2017-10-08 18:35:57 +02:00
for (uint16_t i=0; i<255; i++) { // fade out
for (uint16_t led=0; led<LED_WS2812B_LEDS; led++) { // fade minutes out
if (clock_leds[led*3+1]) {
clock_leds[led*3+1] -= 1; // fade minutes out (green)
}
if (clock_leds[led*3+0]) {
clock_leds[led*3+0] -= 1; // fade seconds out (red)
}
}
clock_leds_set(); // set the colors of all LEDs
led_ws2812b_transmit(); // transmit set color
// delay some time for the animation
for (uint32_t j=0; j<40000; j++) {
__asm__("nop");
}
}
for (uint16_t i=0; i<512; i++) { // fade hour marks 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
2017-10-09 17:57:54 +02:00
uint16_t led = (uint16_t)(LED_WS2812B_LEDS*hour)/12; // 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
led_ws2812b_transmit(); // transmit set color
// delay some time for the animation
for (uint32_t j=0; j<40000; j++) {
__asm__("nop");
}
}
}
2016-08-14 21:02:38 +02:00
/** program entry point
* this is the firmware function started by the micro-controller
*/
2016-10-23 17:42:27 +02:00
void main(void);
void main(void)
{
2016-01-29 00:24:49 +01:00
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
2016-08-14 21:02:38 +02:00
2016-10-23 17:42:27 +02:00
#if DEBUG
2017-01-30 09:44:51 +01:00
// enable functionalities for easier debug
2016-10-23 17:42:27 +02:00
DBGMCU_CR |= DBGMCU_CR_IWDG_STOP; // stop independent watchdog counter when code is halted
DBGMCU_CR |= DBGMCU_CR_WWDG_STOP; // stop window watchdog counter when code is halted
DBGMCU_CR |= DBGMCU_CR_STANDBY; // allow debug also in standby mode (keep digital part and clock powered)
DBGMCU_CR |= DBGMCU_CR_STOP; // allow debug also in stop mode (keep clock powered)
DBGMCU_CR |= DBGMCU_CR_SLEEP; // allow debug also in sleep mode (keep clock powered)
2017-02-06 17:40:28 +01:00
#else
2017-01-30 09:44:51 +01:00
// setup watchdog to reset in case we get stuck (i.e. when an error occurred)
iwdg_set_period_ms(WATCHDOG_PERIOD); // set independent watchdog period
iwdg_start(); // start independent watchdog
2016-10-23 17:42:27 +02:00
#endif
2017-04-03 13:32:45 +02:00
board_setup(); // setup board
2017-10-04 14:02:50 +02:00
usart_setup(); // setup USART (for printing)
2017-04-19 16:26:40 +02:00
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
2017-10-08 18:09:52 +02:00
printf("welcome to the CuVoodoo LED clock\n"); // print welcome message
2016-01-29 00:24:49 +01:00
2017-01-30 09:44:51 +01:00
#if !(DEBUG)
// show watchdog information
printf("watchdog set to (%.2fs)\n",WATCHDOG_PERIOD/1000.0);
if (FLASH_OBR&FLASH_OBR_OPTERR) {
printf("option bytes not set in flash: software wachtdog used (not started at reset)\n");
} else if (FLASH_OBR&FLASH_OBR_WDG_SW) {
printf("software wachtdog used (not started at reset)\n");
} else {
printf("hardware wachtdog used (started at reset)\n");
}
#endif
2016-08-14 21:02:38 +02:00
// setup RTC
printf("setup internal RTC: ");
2017-10-08 18:09:52 +02:00
rtc_auto_awake(RCC_LSE, 32768/TICKS_SECOND-1); // ensure internal RTC is on, uses the 32.678 kHz LSE, and the prescale is set to our tick speed, else update backup registers accordingly (power off the micro-controller for the change to take effect)
2016-08-14 21:02:38 +02:00
rtc_interrupt_enable(RTC_SEC); // enable RTC interrupt on "seconds"
nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt
printf("OK\n");
// setup DCF77
printf("setup DCF77 receiver: ");
rtc_dcf77_setup(); // setup DCF77 module
printf("OK\n");
rtc_dcf77_on(); // switch DCF77 on to get correct time
printf("DCF77 receiver switched on\n"); // notify user
// setup WS2812B LEDs
printf("setup LEDs: ");
for (uint16_t i=0; i<LENGTH(gamma_correction_lut); i++) { // generate gamma correction table
gamma_correction_lut[i] = powf((float)i / (float)LENGTH(gamma_correction_lut), 2.2)*LENGTH(gamma_correction_lut); // calculate using fixed gamma value
}
led_ws2812b_setup(); // setup WS2812B LEDs
clock_clear(); // clear all LEDs
clock_leds_set(); // set the colors of all LEDs
led_ws2812b_transmit(); // transmit set color
printf("OK\n");
// setup ADC to photo-resistor voltage
printf("setup brightness sensor: ");
rcc_periph_clock_enable(RCC_ADC12_IN(PHOTORESISTOR_ADC_CHANNEL)); // enable clock for photo-resistor GPIO peripheral
gpio_set_mode(ADC12_IN_PORT(PHOTORESISTOR_ADC_CHANNEL), GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, ADC12_IN_PIN(PHOTORESISTOR_ADC_CHANNEL)); // 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
// read internal reference 1.2V
uint8_t channels[] = {ADC_CHANNEL17}; // 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
// now use interrupts to only measure ambient luminosity
channels[0] = ADC_CHANNEL(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 conversion
nvic_enable_irq(NVIC_ADC1_2_IRQ); // enable ADC interrupts
printf("OK\n");
// get date and time
uint32_t ticks_time = rtc_get_counter_val(); // get time/date from internal RTC
2017-10-08 18:09:52 +02:00
printf("current time: %02u:%02u:%02u\n", ticks_time/TICKS_HOUR, (ticks_time%TICKS_HOUR)/TICKS_MINUTE, (ticks_time%TICKS_MINUTE)/TICKS_SECOND); // display time
clock_animate_time(ticks_time); // set time with animation
2016-10-23 17:42:27 +02:00
2016-08-14 21:02:38 +02:00
// main loop
printf("command input: ready\n");
2017-10-08 18:09:52 +02:00
led_on(); // indicate everything is OK and the main loop will start
bool action = false; // if an action has been performed don't go to sleep
button_flag = false; // reset button flag
2016-10-23 17:42:27 +02:00
char c = '\0'; // to store received character
2016-08-14 21:02:38 +02:00
bool char_flag = false; // a new character has been received
2016-01-29 00:24:49 +01:00
while (true) { // infinite loop
2016-10-23 17:42:27 +02:00
iwdg_reset(); // kick the dog
2016-08-14 21:02:38 +02:00
while (usart_received) { // data received over UART
action = true; // action has been performed
2016-01-29 00:24:49 +01:00
led_toggle(); // toggle LED
2016-08-14 21:02:38 +02:00
c = usart_getchar(); // store receive character
char_flag = true; // notify character has been received
}
2017-04-19 16:24:07 +02:00
while (usb_cdcacm_received) { // data received over USB
action = true; // action has been performed
2016-01-29 00:24:49 +01:00
led_toggle(); // toggle LED
2017-04-19 16:24:07 +02:00
c = usb_cdcacm_getchar(); // store receive character
2016-08-14 21:02:38 +02:00
char_flag = true; // notify character has been received
2016-01-18 16:23:35 +01:00
}
2016-08-14 21:02:38 +02:00
while (char_flag) { // user data received
char_flag = false; // reset flag
action = true; // action has been performed
2016-08-14 21:02:38 +02:00
printf("%c",c); // echo receive character
if (c=='\r' || 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 { // 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
action = true; // action has been performed
printf("button pressed\n");
led_toggle(); // toggle LED
2016-08-14 21:02:38 +02:00
for (uint32_t i=0; i<1000000; i++) { // wait a bit to remove noise and double trigger
__asm__("nop");
}
button_flag = false; // reset flag
}
while (rtc_dcf77_time_flag) { // the DCF77 module received a new time
rtc_dcf77_time_flag = false; // reset flag
action = true; // action has been performed
2017-10-13 15:50:25 +02:00
if (rtc_dcf77_time.valid) { // ensure decoded time is valid
ticks_time = rtc_dcf77_time.hours*TICKS_HOUR+rtc_dcf77_time.minutes*TICKS_MINUTE+rtc_dcf77_time.seconds*TICKS_SECOND+(rtc_dcf77_time.milliseconds*TICKS_SECOND)/1000; // calculate new time
rtc_set_counter_val(ticks_time); // set new time to internal RTC
2017-10-13 15:50:25 +02:00
printf("DCF77 time: 20%02u-%02u-%02u %02u:%02u:%02u\n", rtc_dcf77_time.year, rtc_dcf77_time.month, rtc_dcf77_time.day, rtc_dcf77_time.hours, rtc_dcf77_time.minutes, rtc_dcf77_time.seconds); // display time
// never switch of DCF77 receiver since the signal gets better demodulated with time, we always have power, we can always get correct the time
} else {
printf("DCF77 time: error\n");
}
}
2016-08-14 21:02:38 +02:00
while (rtc_internal_tick_flag) { // the internal RTC ticked
rtc_internal_tick_flag = false; // reset flag
ticks_time = rtc_get_counter_val(); // copy time from internal RTC for processing
2017-06-27 15:44:03 +02:00
action = true; // action has been performed
2017-10-08 18:09:52 +02:00
if ((ticks_time%(TICKS_SECOND/10))==0) { // one tenth of a second passed
adc_start_conversion_regular(ADC1); // start measuring ambient luminosity
2016-08-14 21:02:38 +02:00
}
2017-10-08 18:09:52 +02:00
if ((ticks_time%TICKS_SECOND)==0) { // one second passed
//led_toggle(); // LED toggling confuses the 32.768 kHz oscillator on the blue pill
}
2017-10-08 18:09:52 +02:00
if ((ticks_time%TICKS_MINUTE)==0) { // one minute passed
printf("%02u:%02u:%02u\n", ticks_time/TICKS_HOUR, (ticks_time%TICKS_HOUR)/TICKS_MINUTE, (ticks_time%TICKS_MINUTE)/TICKS_SECOND); // display external time
}
2017-10-13 15:50:25 +02:00
if ((ticks_time%(TICKS_MINUTE*5))==0) { // five minutes passed
rtc_dcf77_on(); // ensure the module is on in case it switched off because of decoding issues
}
2017-10-08 18:09:52 +02:00
if ((ticks_time%TICKS_HOUR)==0) { // one hours passed
clock_hours(); // show hour markers
}
2017-10-08 18:09:52 +02:00
if (ticks_time>=TICKS_MIDDAY*2) { // one day passed
rtc_set_counter_val(rtc_get_counter_val()%TICKS_MIDDAY); // reset time counter
}
clock_set_time(ticks_time); // 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);
2016-08-14 21:02:38 +02:00
}
if (action) { // go to sleep if nothing had to be done, else recheck for activity
action = false;
} else {
__WFI(); // go to sleep
}
2016-10-23 17:42:27 +02:00
} // main loop
2016-01-17 14:54:54 +01:00
}
2016-01-29 11:25:30 +01:00
2016-08-14 21:02:38 +02:00
/** @brief interrupt service routine called when tick passed on RTC */
void rtc_isr(void)
{
rtc_clear_flag(RTC_SEC); // clear flag
rtc_internal_tick_flag = true; // notify to show new time
}
/** 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
}