application: port LED clock firmware to application
This commit is contained in:
parent
e8826000fa
commit
e70e430a31
372
application.c
372
application.c
|
@ -22,7 +22,7 @@
|
||||||
#include <stdint.h> // standard integer types
|
#include <stdint.h> // standard integer types
|
||||||
#include <stdlib.h> // standard utilities
|
#include <stdlib.h> // standard utilities
|
||||||
#include <string.h> // string utilities
|
#include <string.h> // string utilities
|
||||||
#include <time.h> // date/time utilities
|
#include <math.h> // mathematical utilities
|
||||||
|
|
||||||
/* STM32 (including CM3) libraries */
|
/* STM32 (including CM3) libraries */
|
||||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||||
|
@ -35,12 +35,19 @@
|
||||||
#include <libopencm3/stm32/iwdg.h> // independent watchdog utilities
|
#include <libopencm3/stm32/iwdg.h> // independent watchdog utilities
|
||||||
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
|
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
|
||||||
#include <libopencm3/stm32/flash.h> // flash utilities
|
#include <libopencm3/stm32/flash.h> // flash utilities
|
||||||
|
#include <libopencm3/stm32/adc.h> // ADC utilities
|
||||||
|
#include <libopencm3/stm32/rtc.h> // real time clock utilities
|
||||||
|
|
||||||
/* own libraries */
|
/* own libraries */
|
||||||
#include "global.h" // board definitions
|
#include "global.h" // board definitions
|
||||||
#include "print.h" // printing utilities
|
#include "print.h" // printing utilities
|
||||||
#include "usart.h" // USART utilities
|
#include "usart.h" // USART utilities
|
||||||
#include "usb_cdcacm.h" // USB CDC ACM utilities
|
#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
|
||||||
|
|
||||||
#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */
|
#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */
|
||||||
|
|
||||||
|
@ -48,10 +55,53 @@
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
|
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 */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
time_t time_rtc = 0; /**< time (seconds since Unix Epoch) */
|
/** @defgroup main_ticks ticks per time units
|
||||||
struct tm* time_tm; /**< time in tm format (time zones are not handled for non-POSIX environments) */
|
* @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) */
|
||||||
|
#define TICKS_PER_SECOND 256UL
|
||||||
|
/** 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 photoresistor_adc ADC used to ambient luminosity
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define PHOTORESISTOR_ADC_CHANNEL ADC_CHANNEL1 /**< 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 GPIO1 /**< pin of the port on which the battery is connected */
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** 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
|
||||||
|
|
||||||
size_t putc(char c)
|
size_t putc(char c)
|
||||||
{
|
{
|
||||||
|
@ -99,6 +149,8 @@ static void process_command(char* str)
|
||||||
if (0==strcmp(word,"h") || 0==strcmp(word,"help") || 0==strcmp(word,"?")) {
|
if (0==strcmp(word,"h") || 0==strcmp(word,"help") || 0==strcmp(word,"?")) {
|
||||||
printf("available commands:\n");
|
printf("available commands:\n");
|
||||||
printf("led [on|off|toggle]\n");
|
printf("led [on|off|toggle]\n");
|
||||||
|
printf("time [HH:MM:SS]\n");
|
||||||
|
printf("DCF77 on|off\n");
|
||||||
} else if (0==strcmp(word,"l") || 0==strcmp(word,"led")) {
|
} else if (0==strcmp(word,"l") || 0==strcmp(word,"led")) {
|
||||||
word = strtok(NULL,delimiter);
|
word = strtok(NULL,delimiter);
|
||||||
if (!word) {
|
if (!word) {
|
||||||
|
@ -123,38 +175,25 @@ static void process_command(char* str)
|
||||||
} else if (0==strcmp(word,"time")) {
|
} else if (0==strcmp(word,"time")) {
|
||||||
word = strtok(NULL,delimiter);
|
word = strtok(NULL,delimiter);
|
||||||
if (!word) {
|
if (!word) {
|
||||||
time_rtc = rtc_get_counter_val(); // get time from internal RTC
|
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
|
||||||
time_tm = localtime(&time_rtc); // convert time
|
|
||||||
printf("time: %02d:%02d:%02d\n", time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
|
||||||
} 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
|
} 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;
|
goto error;
|
||||||
} else {
|
} else {
|
||||||
time_rtc = rtc_get_counter_val(); // get time from internal RTC
|
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
|
||||||
time_tm = localtime(&time_rtc); // convert time
|
|
||||||
time_tm->tm_hour = (word[0]-'0')*10+(word[1]-'0')*1; // set hours
|
|
||||||
time_tm->tm_min = (word[3]-'0')*10+(word[4]-'0')*1; // set minutes
|
|
||||||
time_tm->tm_sec = (word[6]-'0')*10+(word[7]-'0')*1; // set seconds
|
|
||||||
time_rtc = mktime(time_tm); // get back seconds
|
|
||||||
rtc_set_counter_val(time_rtc); // save time to internal RTC
|
|
||||||
printf("time set\n");
|
printf("time set\n");
|
||||||
}
|
}
|
||||||
} else if (0==strcmp(word,"date")) {
|
} else if (0==strcmp(word,"DCF77")) {
|
||||||
word = strtok(NULL,delimiter);
|
word = strtok(NULL,delimiter);
|
||||||
if (!word) {
|
if (!word) {
|
||||||
time_rtc = rtc_get_counter_val(); // get time from internal RTC
|
|
||||||
time_tm = localtime(&time_rtc); // convert time
|
|
||||||
printf("date: %d-%02d-%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon+1, time_tm->tm_mday);
|
|
||||||
} 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;
|
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
|
||||||
} else {
|
} else {
|
||||||
time_rtc = rtc_get_counter_val(); // get time from internal RTC
|
goto error;
|
||||||
time_tm = localtime(&time_rtc); // convert time
|
|
||||||
time_tm->tm_year = ((word[0]-'0')*1000+(word[1]-'0')*100+(word[2]-'0')*10+(word[3]-'0')*1)-1900; // set year
|
|
||||||
time_tm->tm_mon = (word[5]-'0')*10+(word[6]-'0')*1-1; // set month
|
|
||||||
time_tm->tm_mday = (word[8]-'0')*10+(word[9]-'0')*1; // set day
|
|
||||||
time_rtc = mktime(time_tm); // get back seconds
|
|
||||||
rtc_set_counter_val(time_rtc); // save time to internal RTC
|
|
||||||
printf("date set\n");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -166,6 +205,162 @@ error:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 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)
|
||||||
|
{
|
||||||
|
uint32_t led_hour = (LED_WS2812B_LEDS*(256*(uint64_t)(time%ticks_midday)))/ticks_midday; // scale to LED brightnesses for hours
|
||||||
|
uint32_t led_minute = (LED_WS2812B_LEDS*(256*(uint64_t)(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++) {
|
||||||
|
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<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<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<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 = (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
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
clock_show_time(time); // set time
|
||||||
|
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) {
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 = 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
|
||||||
|
led_ws2812b_transmit(); // transmit set color
|
||||||
|
// delay some time for the animation
|
||||||
|
for (uint32_t j=0; j<40000; j++) {
|
||||||
|
__asm__("nop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** program entry point
|
/** program entry point
|
||||||
* this is the firmware function started by the micro-controller
|
* this is the firmware function started by the micro-controller
|
||||||
*/
|
*/
|
||||||
|
@ -207,14 +402,64 @@ void main(void)
|
||||||
|
|
||||||
// setup RTC
|
// setup RTC
|
||||||
printf("setup internal RTC: ");
|
printf("setup internal RTC: ");
|
||||||
rtc_auto_awake(RCC_LSE, 32768-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)
|
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)
|
||||||
rtc_interrupt_enable(RTC_SEC); // enable RTC interrupt on "seconds"
|
rtc_interrupt_enable(RTC_SEC); // enable RTC interrupt on "seconds"
|
||||||
nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt
|
nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt
|
||||||
printf("OK\n");
|
printf("OK\n");
|
||||||
|
|
||||||
time_rtc= rtc_get_counter_val(); // get time from internal RTC
|
// setup DCF77
|
||||||
time_tm = localtime(&time_rtc); // convert time
|
printf("setup DCF77 receiver: ");
|
||||||
printf("date: %d-%02d-%02d %02d:%02d:%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon+1, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
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(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
|
||||||
|
// 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] = 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
|
||||||
|
printf("current time: %02lu:%02lu:%02lu\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
|
||||||
|
|
||||||
// main loop
|
// main loop
|
||||||
printf("command input: ready\n");
|
printf("command input: ready\n");
|
||||||
|
@ -262,17 +507,63 @@ void main(void)
|
||||||
}
|
}
|
||||||
button_flag = false; // reset flag
|
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
|
||||||
|
uint8_t* dcf77_time = rtc_dcf77_time(); // get time
|
||||||
|
if (dcf77_time) { // ensure it's valid
|
||||||
|
ticks_time = dcf77_time[1]*ticks_hour+dcf77_time[0]*ticks_minute; // calculate new time
|
||||||
|
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
|
||||||
|
rtc_ds1307_ticks = ticks_time; // set new time
|
||||||
|
rtc_ds1307_write_time(0, dcf77_time[0], dcf77_time[1], ((dcf77_time[3]+1)%8)+1, dcf77_time[2], dcf77_time[4], dcf77_time[5]); // set date and time
|
||||||
|
rtc_ds1307_oscillator_enable(); // be sure the oscillation is enabled
|
||||||
|
#else
|
||||||
|
rtc_set_counter_val(ticks_time); // set new time to internal RTC
|
||||||
|
#endif
|
||||||
|
printf("DCF77 time: 20%02u-%02u-%02u %02u:%02u:00\n", dcf77_time[5], dcf77_time[4], dcf77_time[2], dcf77_time[1], dcf77_time[0]); // display time
|
||||||
|
rtc_dcf77_off(); // switch DCF77 off since we have correct time
|
||||||
|
printf("DCF77 receiver switched off\n"); // notify user
|
||||||
|
} else {
|
||||||
|
printf("DCF77 time: error\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
while (rtc_internal_tick_flag) { // the internal RTC ticked
|
while (rtc_internal_tick_flag) { // the internal RTC ticked
|
||||||
rtc_internal_tick_flag = false; // reset flag
|
rtc_internal_tick_flag = false; // reset flag
|
||||||
|
ticks_time = rtc_get_counter_val(); // copy time from internal RTC for processing
|
||||||
action = true; // action has been performed
|
action = true; // action has been performed
|
||||||
#if !defined(BLUE_PILL) // on the blue pill the LED is close to the 32.768 kHz oscillator and heavily influences it
|
if ((ticks_time%(ticks_second/10))==0) { // one tenth of a second passed
|
||||||
led_toggle(); // toggle LED (good to indicate if main function is stuck)
|
adc_start_conversion_regular(ADC1); // start measuring ambient luminosity
|
||||||
#endif
|
|
||||||
time_rtc = rtc_get_counter_val(); // get time from internal RTC (seconds since Unix Epoch)
|
|
||||||
time_tm = localtime(&time_rtc); // get time in tm format from Epoch (time zones are not handled for non-POSIX environments)
|
|
||||||
if (0==time_tm->tm_sec) { // new minute
|
|
||||||
printf("time: %02d:%02d:%02d\n", time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
|
||||||
}
|
}
|
||||||
|
if ((ticks_time%ticks_second)==0) { // one second passed
|
||||||
|
led_toggle(); // LED toggling confuses the 32.768 kHz oscillator on the blue pill
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if ((ticks_time%ticks_hour)==0) { // one hours passed
|
||||||
|
clock_hours(); // show hour markers
|
||||||
|
rtc_dcf77_on(); // switch DCF77 on to update/correct time
|
||||||
|
printf("DCF77 receiver switched on\n"); // notify user
|
||||||
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
if (action) { // go to sleep if nothing had to be done, else recheck for activity
|
if (action) { // go to sleep if nothing had to be done, else recheck for activity
|
||||||
action = false;
|
action = false;
|
||||||
|
@ -288,3 +579,10 @@ void rtc_isr(void)
|
||||||
rtc_clear_flag(RTC_SEC); // clear flag
|
rtc_clear_flag(RTC_SEC); // clear flag
|
||||||
rtc_internal_tick_flag = true; // notify to show new time
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue