led_clock-old/main.c

655 lines
27 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/>.
*
*/
2016-05-05 23:47:50 +02:00
/** show the time on a LED strip
* @file main.c
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016
2016-03-23 07:50:53 +01:00
*/
2016-01-17 14:54:54 +01:00
/* 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
2016-03-26 19:48:25 +01:00
#include <string.h> // string utilities
2016-03-28 16:39:11 +02:00
#include <math.h> // mathematical utilities
2016-01-17 14:54:54 +01:00
/* 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
2016-01-18 16:23:35 +01:00
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
2016-01-29 11:25:30 +01:00
#include <libopencm3/cm3/nvic.h> // interrupt utilities
#include <libopencm3/stm32/exti.h> // external interrupt utilities
2016-04-03 15:39:55 +02:00
#include <libopencm3/stm32/adc.h> // ADC utilities
2016-04-03 21:29:04 +02:00
#include <libopencm3/stm32/rtc.h> // real time clock utilities
2016-01-17 14:54:54 +01:00
/* own libraries */
#include "global.h" // board definitions
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
2016-06-12 23:11:37 +02:00
#include "led_ws2812b.h" // WS2812B LEDs utilities
2016-04-30 12:11:54 +02:00
#include "rtc_ds1307.h" // Real Time Clock DS1307 utilities
2016-05-01 20:46:36 +02:00
#include "rtc_dcf77.h" // DCF77 time receiver utilities
2016-01-17 14:54:54 +01:00
/** use external RTC, else use internal RTC */
#define EXTERNAL_RTC false
2016-03-24 10:37:42 +01:00
/** @defgroup main_flags flag set in interrupts to be processed in main task
* @{
2016-03-24 10:37:42 +01:00
*/
volatile bool button_flag = false; /**< flag set when board user button has been pressed/released */
volatile bool photoresistor_flag = false; /**< flag set when ambient luminosity is measured */
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
#else
volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
#endif
2016-03-24 10:37:42 +01:00
/** @} */
2016-02-18 11:34:08 +01:00
2016-03-23 07:50:53 +01:00
/** @defgroup main_ticks ticks per time units
2016-03-25 19:19:34 +01:00
* @note these are derived from TICKS_PER_SECOND
2016-03-23 07:50:53 +01:00
* @note I have to use type variables because defines would be stored in signed integers, leading to an overflow it later calculations
* @{
*/
2016-05-01 20:35:11 +02:00
/** the number of ticks in one second (32768 divisor greater than 256*LED_WS2812B_LEDS/60) */
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
2016-05-01 20:35:11 +02:00
#define TICKS_PER_SECOND (RTC_DS1307_SQUARE_WAVE_FREQUENCY/RTC_DS1307_SQUARE_WAVE_TICKS)
#else
#define TICKS_PER_SECOND 256
#endif
2016-03-23 07:50:53 +01:00
/** 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;
/** @} */
2016-02-19 11:31:30 +01:00
2016-04-04 10:52:31 +02:00
/** @defgroup photoresistor_adc ADC used to ambient luminosity
* @{
*/
2016-04-30 12:11:54 +02:00
#define PHOTORESISTOR_ADC_CHANNEL ADC_CHANNEL1 /**< ADC channel */
2016-04-04 10:52:31 +02:00
#define PHOTORESISTOR_PORT GPIOA /**< port on which the battery is connected */
#define PHOTORESISTOR_PORT_RCC RCC_GPIOA /**< timer port peripheral clock */
2016-04-30 12:11:54 +02:00
#define PHOTORESISTOR_PIN GPIO1 /**< pin of the port on which the battery is connected */
2016-04-04 10:52:31 +02:00
/** @} */
2016-04-03 15:39:55 +02:00
2016-06-12 23:11:37 +02:00
/** RGB values for the WS2812B clock LEDs */
2016-05-01 15:03:41 +02:00
uint8_t clock_leds[LED_WS2812B_LEDS*3] = {0};
2016-03-26 19:48:25 +01:00
/** user input command */
char command[32] = {0};
/** user input command index */
uint8_t command_i = 0;
2016-03-28 16:39:11 +02:00
/** 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;
2016-04-04 21:33:30 +02:00
/** 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 */
2016-04-04 21:52:11 +02:00
float clock_brightness = 1;
2016-04-04 21:33:30 +02:00
/** minimum LED brightness */
#define BRIGHTNESS_MIN 0.2
/** maximum LED brightness */
#define BRIGHTNESS_MAX 1.0
2016-04-04 21:52:11 +02:00
/** the factor to change the brightness */
#define BRIGHTNESS_FACTOR 0.1
2016-01-17 14:54:54 +01:00
int _write(int file, char *ptr, int len)
{
2016-04-28 11:18:10 +02:00
int i; // how much data has been sent
static char newline = 0; // what newline has been sent
2016-01-17 14:54:54 +01:00
if (file == STDOUT_FILENO || file == STDERR_FILENO) {
for (i = 0; i < len; i++) {
2016-04-30 14:47:40 +02:00
if (ptr[i] == '\r' || ptr[i] == '\n') { // send CR+LF newline for most carriage return and line feed combination
2016-04-28 11:18:10 +02:00
if (newline==0 || (newline==ptr[i])) { // newline has already been detected
usart_putchar_nonblocking('\r'); // send newline over USART
usart_putchar_nonblocking('\n'); // send newline over USART
cdcacm_putchar('\r'); // send newline over USB
cdcacm_putchar('\n'); // send newline over USB
newline = ptr[i]; // remember the newline
}
2016-04-30 14:47:40 +02:00
if (ptr[i] == '\n') { // line feed are always considered to end a line (the LF+CR combination is not supported to better support the others)
newline = 0; // clear new line
}
2016-04-28 11:18:10 +02:00
} else { // non-newline character
2016-04-30 14:47:40 +02:00
usart_putchar_nonblocking(ptr[i]); // send byte over USART
2016-04-28 11:18:10 +02:00
cdcacm_putchar(ptr[i]); // send byte over USB
newline = 0; // clear new line
2016-01-17 14:54:54 +01:00
}
}
return i;
}
errno = EIO;
return -1;
}
2016-05-05 23:30:25 +02:00
char* b2s(uint64_t binary, uint8_t rjust)
2016-04-09 23:19:19 +02:00
{
2016-05-05 23:30:25 +02:00
static char string[64+1] = {0}; // the string representation to return
int8_t bit = LENGTH(string)-1; // the index of the bit to print
2016-04-09 23:19:19 +02:00
string[bit--] = 0; // terminate string
while (binary) {
if (binary & 1) {
string[bit--] = '1';
} else {
string[bit--] = '0';
}
binary >>= 1;
}
2016-05-05 23:30:25 +02:00
while (64-bit-1<rjust && bit>=0) {
2016-04-09 23:19:19 +02:00
string[bit--] = '0';
}
return &string[bit+1];
}
2016-05-05 23:47:50 +02:00
/** switch off all clock LEDs
2016-03-25 11:42:41 +01:00
* @note LEDs need to be set separately
*/
2016-02-19 11:31:30 +01:00
static void clock_clear(void)
2016-02-18 11:34:08 +01:00
{
// set all colors of all LEDs to 0
for (uint16_t i=0; i<LENGTH(clock_leds); i++) {
clock_leds[i] = 0;
}
}
2016-05-05 23:47:50 +02:00
/** show time on LED clock
2016-03-25 11:42:41 +01:00
* @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
*/
2016-02-24 10:33:21 +01:00
static void clock_show_time(uint32_t time)
2016-02-19 11:31:30 +01:00
{
2016-05-01 15:03:41 +02:00
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
2016-02-23 20:28:55 +01:00
return;
2016-02-19 11:31:30 +01:00
}
2016-02-23 23:38:08 +01:00
// show hours and minutes on LEDs
if (led_hour>led_minute) {
// show hours in blue (and clear other LEDs)
2016-05-01 15:03:41 +02:00
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)
2016-05-01 15:03:41 +02:00
for (uint16_t led=0; led<LED_WS2812B_LEDS && led_minute>0; led++) {
2016-02-23 21:52:00 +01:00
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)
2016-05-01 15:03:41 +02:00
for (uint16_t led=0; led<LED_WS2812B_LEDS; led++) {
2016-02-23 21:52:00 +01:00
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;
2016-02-23 21:52:00 +01:00
}
led_minute -= clock_leds[led*3+1];
clock_leds[led*3+2] = 0;
}
// show hours in blue (override minutes)
2016-05-01 15:03:41 +02:00
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;
2016-02-23 21:52:00 +01:00
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];
2016-02-23 20:28:55 +01:00
}
}
2016-02-24 10:33:21 +01:00
// don't show seconds on full minute (better for first time setting, barely visible else)
if (time%ticks_minute==0) {
return;
}
2016-05-01 15:03:41 +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
2016-05-01 15:03:41 +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
2016-05-01 15:03:41 +02:00
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)
2016-02-19 11:31:30 +01:00
}
2016-05-05 23:47:50 +02:00
/** set the LEDs
2016-06-12 23:11:37 +02:00
* @details set the LED colors on WS2812B LEDs
* @note WS2812B LED color values need to be transmitted separately
2016-03-25 11:42:41 +01:00
*/
2016-03-26 14:58:21 +01:00
static void clock_leds_set(void)
2016-02-18 11:34:08 +01:00
{
for (uint16_t i=0; i<LENGTH(clock_leds)/3; i++) {
2016-05-01 15:03:41 +02:00
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)
2016-02-18 11:34:08 +01:00
}
}
2016-05-05 23:47:50 +02:00
/** set the time on the LEDs
2016-03-25 13:57:56 +01:00
* @param[in] time time to set
*/
static void clock_set_time(uint32_t time)
2016-03-25 20:24:46 +01:00
{
clock_show_time(time); // set time
2016-03-26 14:58:21 +01:00
clock_leds_set(); // set the colors of all LEDs
2016-05-01 15:03:41 +02:00
led_ws2812b_transmit(); // transmit set color
2016-03-25 20:24:46 +01:00
}
2016-05-05 23:47:50 +02:00
/** incrementally set the time on the LEDs
2016-03-25 20:24:46 +01:00
* @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)
2016-03-25 13:57:56 +01:00
{
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;
}
2016-03-25 20:24:46 +01:00
clock_set_time(display_time); // set time (progress)
2016-03-25 13:57:56 +01:00
// delay some time for the animation
for (uint32_t i=0; i<400000; i++) {
__asm__("nop");
}
}
}
2016-05-05 23:47:50 +02:00
/** show animation with fading hours mark on clock LEDs
2016-03-28 17:01:39 +02:00
*/
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
2016-05-01 15:03:41 +02:00
uint16_t led = LED_WS2812B_LEDS/12*hour; // get LED four hour mark
2016-03-28 17:01:39 +02:00
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
2016-05-01 15:03:41 +02:00
led_ws2812b_transmit(); // transmit set color
2016-03-28 17:01:39 +02:00
// delay some time for the animation
for (uint32_t j=0; j<40000; j++) {
__asm__("nop");
}
}
}
2016-05-05 23:47:50 +02:00
/** process user command
2016-03-26 19:48:25 +01:00
* @param[in] str user command string (\0 ended)
*/
static void process_command(char* str)
{
2016-04-13 21:00:59 +02:00
// split command
2016-03-26 19:48:25 +01:00
const char* delimiter = " ";
char* word = strtok(str,delimiter);
if (!word) {
goto error;
}
2016-04-13 21:00:59 +02:00
// parse command
2016-03-26 19:48:25 +01:00
if (0==strcmp(word,"help")) {
printf("available commands:\n");
printf("time [HH:MM:SS]\n");
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
printf("date [YYYY-MM-DD]\n");
#endif
2016-03-26 19:48:25 +01:00
} else if (0==strcmp(word,"time")) {
word = strtok(NULL,delimiter);
if (!word) {
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
printf("current time: %02u:%02u:%02u\n", rtc_ds1307_read_hours(), rtc_ds1307_read_minutes(), rtc_ds1307_read_seconds()); // get and print time from external RTC
#else
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); // get and print time from internal RTC
#endif
} 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
2016-03-26 19:48:25 +01:00
goto error;
} else {
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
if (!rtc_ds1307_write_hours((word[0]-'0')*10+(word[1]-'0')*1)) {
printf("setting hours failed\n");
} else if (!rtc_ds1307_write_minutes((word[3]-'0')*10+(word[4]-'0')*1)) {
printf("setting minutes failed\n");
} else if (!rtc_ds1307_write_seconds((word[6]-'0')*10+(word[7]-'0')*1)) {
printf("setting seconds failed\n");
} else {
rtc_ds1307_ticks = rtc_ds1307_read_hours()*ticks_hour+rtc_ds1307_read_minutes()*ticks_minute+rtc_ds1307_read_seconds()*ticks_second; // set also internal time
rtc_ds1307_oscillator_enable(); // be sure the oscillation is enabled
printf("time set\n");
}
#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 internal RTC counter
2016-04-03 21:50:22 +02:00
printf("time set\n");
#endif
}
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
} else if (0==strcmp(word,"date")) {
word = strtok(NULL,delimiter);
if (!word) {
printf("current date: 20%02u-%02u-%02u\n", rtc_ds1307_read_year(), rtc_ds1307_read_month(), rtc_ds1307_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_ds1307_write_year((word[2]-'0')*10+(word[3]-'0')*1)) {
printf("setting year failed\n");
} else if (!rtc_ds1307_write_month((word[5]-'0')*10+(word[6]-'0')*1)) {
printf("setting month failed\n");
} else if (!rtc_ds1307_write_date((word[8]-'0')*10+(word[9]-'0')*1)) {
printf("setting day failed\n");
} else {
printf("date set\n");
}
}
#endif
2016-03-26 19:48:25 +01:00
} else {
goto error;
}
return; // command successfully processed
error:
2016-04-28 11:18:10 +02:00
printf("command not recognized. enter help to list commands\n");
2016-03-26 19:48:25 +01:00
}
2016-05-05 23:47:50 +02:00
/** program entry point
2016-03-24 10:37:42 +01:00
* this is the firmware function started by the micro-controller
*/
2016-01-17 14:54:54 +01:00
int 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-05-01 20:35:11 +02:00
// 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 per default
// setup USART and USB for user communication
2016-01-29 11:25:30 +01:00
usart_setup(); // setup USART (for printing)
2016-04-13 21:00:59 +02:00
cdcacm_setup(); // setup USB CDC ACM (for printing)
2016-01-29 11:25:30 +01:00
setbuf(stdout, NULL); // set standard out buffer to NULL to immediately print
setbuf(stderr, NULL); // set standard error buffer to NULL to immediately print
2016-01-29 00:24:49 +01:00
2016-05-01 20:35:11 +02:00
// minimal setup ready
printf("welcome to the CuVoodoo LED clock\n"); // print welcome message
2016-01-29 00:24:49 +01:00
2016-01-29 11:25:30 +01:00
// 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
2016-04-17 12:16:23 +02:00
gpio_set_mode(BUTTON_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, BUTTON_PIN); // set button pin to input
gpio_clear(BUTTON_PORT, BUTTON_PIN); // pull down to be able to detect button push (go high)
2016-01-29 11:25:30 +01:00
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
2016-05-01 20:35:11 +02:00
exti_set_trigger(BUTTON_EXTI, EXTI_TRIGGER_RISING); // trigger when button is pressed
2016-01-29 11:25:30 +01:00
exti_enable_request(BUTTON_EXTI); // enable external interrupt
nvic_enable_irq(BUTTON_IRQ); // enable interrupt
#endif
2016-01-17 14:54:54 +01:00
2016-05-01 20:35:11 +02:00
// setup RTC
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
printf("setup external RTC: ");
2016-05-01 20:35:11 +02:00
rtc_ds1307_setup(); // setup external RTC module
#else
printf("setup internal RTC: ");
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"
nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt
#endif
2016-05-01 20:46:36 +02:00
rtc_dcf77_setup(); // setup DCF77 module
rtc_dcf77_on(); // switch on DCF77 module to get correct time
2016-05-01 20:35:11 +02:00
printf("OK\n");
2016-03-28 16:39:11 +02:00
2016-06-12 23:11:37 +02:00
// setup WS2812B LEDs
2016-05-01 20:35:11 +02:00
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
}
2016-06-12 23:11:37 +02:00
led_ws2812b_setup(); // setup WS2812B LEDs
2016-02-24 10:33:21 +01:00
clock_clear(); // clear all LEDs
2016-03-26 14:58:21 +01:00
clock_leds_set(); // set the colors of all LEDs
2016-05-01 15:03:41 +02:00
led_ws2812b_transmit(); // transmit set color
2016-05-01 20:35:11 +02:00
printf("OK\n");
2016-02-18 11:02:28 +01:00
2016-04-30 12:18:32 +02:00
// setup ADC to photo-resistor voltage
2016-05-01 20:35:11 +02:00
printf("setup brightness sensor: ");
2016-04-04 10:52:31 +02:00
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
2016-04-03 15:39:55 +02:00
// configuration is correct per default
2016-04-04 10:52:31 +02:00
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
2016-04-04 22:10:13 +02:00
for (uint32_t i=0; i<800000; i++) { // wait t_stab for the ADC to stabilize
2016-04-03 15:39:55 +02:00
__asm__("nop");
}
2016-04-04 10:52:31 +02:00
adc_reset_calibration(ADC1); // remove previous non-calibration
adc_calibration(ADC1); // calibrate ADC for less accuracy errors
2016-04-30 12:18:32 +02:00
// read internal reference 1.2V
uint8_t channels[] = {ADC_CHANNEL17}; // voltages to convert
2016-04-04 10:52:31 +02:00
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
2016-04-30 12:18:32 +02:00
adc_enable_eoc_interrupt(ADC1); // enable interrupt for end of conversion
nvic_enable_irq(NVIC_ADC1_2_IRQ); // enable ADC interrupts
2016-05-01 20:35:11 +02:00
printf("OK\n");
2016-04-30 12:18:32 +02:00
// verify is external RTC is running
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
2016-05-01 20:35:11 +02:00
if (rtc_ds1307_oscillator_disabled()) {
2016-04-30 12:18:32 +02:00
printf("/!\\ RTC oscillator is disabled: the battery may be empty\n");
}
#endif
2016-04-30 14:47:40 +02:00
2016-03-25 20:24:46 +01:00
// get date and time
uint32_t ticks_time = 0;
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
uint8_t* rtc_ds1307_time = rtc_ds1307_read_time(); // get time/date from external RTC
rtc_ds1307_ticks = rtc_ds1307_time[2]*ticks_hour+rtc_ds1307_time[1]*ticks_minute+rtc_ds1307_time[0]*ticks_second; // initialize time for external RTC counter
ticks_time = rtc_ds1307_ticks; // save time
printf("current date: 20%02u-%02u-%02u\n", rtc_ds1307_time[6], rtc_ds1307_time[5], rtc_ds1307_time[4]);
#else
ticks_time = rtc_get_counter_val(); // get time/date from internal RTC
#endif
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
2016-03-25 13:57:56 +01:00
// main loop
printf("command input: ready\n");
bool action = false; // if an action has been performed don't go to sleep
button_flag = false; // reset button flag
2016-03-25 13:57:56 +01:00
char c = ' '; // to store received character
2016-03-25 18:00:44 +01:00
bool char_flag = false; // a new character has been received
2016-01-29 00:24:49 +01:00
while (true) { // infinite loop
2016-03-25 19:19:34 +01: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-02-18 11:02:28 +01:00
c = usart_getchar(); // store receive character
char_flag = true; // notify character has been received
}
2016-03-25 19:19:34 +01:00
while (cdcacm_received) { // data received over USB
action = true; // action has been performed
2016-01-29 00:24:49 +01:00
led_toggle(); // toggle LED
2016-04-10 14:31:05 +02:00
c = cdcacm_getchar(); // store receive character
2016-02-18 11:02:28 +01:00
char_flag = true; // notify character has been received
}
2016-03-25 19:19:34 +01:00
while (char_flag) { // user data received
2016-02-18 11:02:28 +01:00
char_flag = false; // reset flag
action = true; // action has been performed
printf("%c",c); // echo receive character
2016-04-28 11:18:10 +02:00
if (c=='\r' || c=='\n') { // end of command received
2016-03-26 19:48:25 +01:00
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
}
2016-04-28 11:18:10 +02:00
} else { // user command input
2016-03-26 19:48:25 +01:00
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
}
}
2016-01-18 16:23:35 +01:00
}
2016-03-25 19:19:34 +01:00
while (button_flag) { // user pressed button
action = true; // action has been performed
2016-04-17 12:16:23 +02:00
printf("time incremented by 1 second\n");
rtc_set_counter_val(rtc_get_counter_val()+ticks_second); // increment time
//led_toggle(); // toggle LED
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
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
} else {
printf("DCF77 time: error\n");
}
}
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
while (rtc_ds1307_tick_flag) { // the RTC tick for our counter passed
2016-05-01 20:35:11 +02:00
rtc_ds1307_tick_flag = false; // reset flag
ticks_time = rtc_ds1307_ticks; // copy time incremented by external RTC for processing
#else
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
#endif
2016-04-30 12:11:54 +02:00
action = true; // action has been performed
if ((ticks_time%(ticks_second/10))==0) { // one tenth of a second passed
adc_start_conversion_regular(ADC1); // start measuring ambient luminosity
}
if ((ticks_time%ticks_second)==0) { // one second passed
2016-06-11 20:14:19 +02:00
led_toggle(); // LED toggling confuses the 32.768 kHz oscillator on the blue pill
2016-03-28 17:01:39 +02:00
}
if ((ticks_time%ticks_minute)==0) { // one minute passed
printf("%02lu:%02lu:%02lu\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
rtc_dcf77_on(); // switch DCF77 on to update/correct time
2016-03-28 17:01:39 +02:00
clock_hours(); // show hour markers
}
if (ticks_time>=ticks_midday*2) { // one day passed
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
rtc_ds1307_ticks = rtc_ds1307_ticks%ticks_midday; // reset time counter
#else
2016-04-03 21:50:22 +02:00
rtc_set_counter_val(rtc_get_counter_val()%ticks_midday); // reset time counter
#endif
2016-04-03 21:29:04 +02:00
}
clock_set_time(ticks_time); // set time
2016-05-05 23:30:25 +02:00
}
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
2016-04-04 21:52:11 +02:00
float new_clock_brightness = 0; // to calculate new brightness
2016-04-04 21:33:30 +02:00
if (photoresistor_voltage<PHOTORESISTOR_MAX) { // high ambient luminosity
2016-04-04 21:52:11 +02:00
new_clock_brightness = BRIGHTNESS_MAX; // set highest brightness
2016-04-04 21:33:30 +02:00
} else if (photoresistor_voltage>PHOTORESISTOR_MIN) { // low ambient luminosity
2016-04-04 21:52:11 +02:00
new_clock_brightness = BRIGHTNESS_MIN; // set low brightness
} else { // intermediate ambient luminosity
2016-04-04 21:52:11 +02:00
new_clock_brightness = BRIGHTNESS_MIN+(BRIGHTNESS_MAX-BRIGHTNESS_MIN)*(1-(photoresistor_voltage-PHOTORESISTOR_MAX)/(PHOTORESISTOR_MIN-PHOTORESISTOR_MAX)); // set variable brightness
}
2016-04-04 21:52:11 +02:00
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-03-25 18:00:44 +01:00
}
2016-03-25 19:19:34 +01:00
if (action) { // go to sleep if nothing had to be done, else recheck for activity
action = false;
} else {
__WFI(); // go to sleep
}
2016-01-17 14:54:54 +01:00
}
return 0;
}
2016-01-29 11:25:30 +01:00
#if defined(BUTTON_ISR) && defined(BUTTON_EXTI)
2016-05-05 23:47:50 +02:00
/** interrupt service routine called when button is pressed */
2016-01-29 11:25:30 +01:00
void BUTTON_ISR(void)
{
exti_reset_request(BUTTON_EXTI); // reset interrupt
button_flag = true; // perform button action
2016-01-29 11:25:30 +01:00
}
#endif
2016-04-03 21:29:04 +02:00
2016-05-05 23:47:50 +02:00
/** 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
}
#if defined(EXTERNAL_RTC) && EXTERNAL_RTC
#else
/** @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
}
#endif