LED clock
 All Files Functions Variables Macros Groups
main.c
Go to the documentation of this file.
1 /* This program is free software: you can redistribute it and/or modify
2  * it under the terms of the GNU General Public License as published by
3  * the Free Software Foundation, either version 3 of the License, or
4  * (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program. If not, see <http://www.gnu.org/licenses/>.
13  *
14  */
21 /* standard libraries */
22 #include <stdint.h> // standard integer types
23 #include <stdio.h> // standard I/O facilities
24 #include <stdlib.h> // standard utilities
25 #include <unistd.h> // standard streams
26 #include <errno.h> // error number utilities
27 #include <string.h> // string utilities
28 #include <math.h> // mathematical utilities
29 
30 /* STM32 (including CM3) libraries */
31 #include <libopencm3/stm32/rcc.h> // real-time control clock library
32 #include <libopencm3/stm32/gpio.h> // general purpose input output library
33 #include <libopencm3/cm3/scb.h> // vector table definition
34 #include <libopencmsis/core_cm3.h> // Cortex M3 utilities
35 #include <libopencm3/cm3/nvic.h> // interrupt utilities
36 #include <libopencm3/stm32/exti.h> // external interrupt utilities
37 #include <libopencm3/stm32/adc.h> // ADC utilities
38 #include <libopencm3/stm32/rtc.h> // real time clock utilities
39 
40 /* own libraries */
41 #include "global.h" // board definitions
42 #include "usart.h" // USART utilities
43 #include "usb_cdcacm.h" // USB CDC ACM utilities
44 #include "led_ws2812b.h" // WS2812B LEDs utilities
45 #include "rtc_ds1307.h" // Real Time Clock DS1307 utilities
46 #include "rtc_dcf77.h" // DCF77 time receiver utilities
47 
49 #define EXTERNAL_RTC false
50 
54 volatile bool button_flag = false;
55 volatile bool photoresistor_flag = false;
56 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
57 #else
58 volatile bool rtc_internal_tick_flag = false;
59 #endif
60 
68 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
69 #define TICKS_PER_SECOND (RTC_DS1307_SQUARE_WAVE_FREQUENCY/RTC_DS1307_SQUARE_WAVE_TICKS)
70 #else
71 #define TICKS_PER_SECOND 256
72 #endif
73 
74 const uint32_t ticks_second = TICKS_PER_SECOND;
76 const uint32_t ticks_minute = 60*TICKS_PER_SECOND;
78 const uint32_t ticks_hour = 60*60*TICKS_PER_SECOND;
80 const uint32_t ticks_midday = 12*60*60*TICKS_PER_SECOND;
86 #define PHOTORESISTOR_ADC_CHANNEL ADC_CHANNEL1
87 #define PHOTORESISTOR_PORT GPIOA
88 #define PHOTORESISTOR_PORT_RCC RCC_GPIOA
89 #define PHOTORESISTOR_PIN GPIO1
94 uint8_t clock_leds[LED_WS2812B_LEDS*3] = {0};
95 
96 char command[32] = {0};
98 uint8_t command_i = 0;
100 uint8_t gamma_correction_lut[256] = {0};
102 volatile uint16_t photoresistor_value = 0;
104 #define PHOTORESISTOR_MIN 2.7
105 
106 #define PHOTORESISTOR_MAX 1.7
107 
110 #define BRIGHTNESS_MIN 0.2
111 
112 #define BRIGHTNESS_MAX 1.0
113 
114 #define BRIGHTNESS_FACTOR 0.1
115 
116 int _write(int file, char *ptr, int len)
117 {
118  int i; // how much data has been sent
119  static char newline = 0; // what newline has been sent
120 
121  if (file == STDOUT_FILENO || file == STDERR_FILENO) {
122  for (i = 0; i < len; i++) {
123  if (ptr[i] == '\r' || ptr[i] == '\n') { // send CR+LF newline for most carriage return and line feed combination
124  if (newline==0 || (newline==ptr[i])) { // newline has already been detected
125  usart_putchar_nonblocking('\r'); // send newline over USART
126  usart_putchar_nonblocking('\n'); // send newline over USART
127  cdcacm_putchar('\r'); // send newline over USB
128  cdcacm_putchar('\n'); // send newline over USB
129  newline = ptr[i]; // remember the newline
130  }
131  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)
132  newline = 0; // clear new line
133  }
134  } else { // non-newline character
135  usart_putchar_nonblocking(ptr[i]); // send byte over USART
136  cdcacm_putchar(ptr[i]); // send byte over USB
137  newline = 0; // clear new line
138  }
139  }
140  return i;
141  }
142  errno = EIO;
143  return -1;
144 }
145 
146 char* b2s(uint64_t binary, uint8_t rjust)
147 {
148  static char string[64+1] = {0}; // the string representation to return
149  int8_t bit = LENGTH(string)-1; // the index of the bit to print
150  string[bit--] = 0; // terminate string
151 
152  while (binary) {
153  if (binary & 1) {
154  string[bit--] = '1';
155  } else {
156  string[bit--] = '0';
157  }
158  binary >>= 1;
159  }
160 
161  while (64-bit-1<rjust && bit>=0) {
162  string[bit--] = '0';
163  }
164 
165  return &string[bit+1];
166 }
167 
171 static void clock_clear(void)
172 {
173  // set all colors of all LEDs to 0
174  for (uint16_t i=0; i<LENGTH(clock_leds); i++) {
175  clock_leds[i] = 0;
176  }
177 }
178 
184 static void clock_show_time(uint32_t time)
185 {
186  uint32_t led_hour = (LED_WS2812B_LEDS*(256*(uint64_t)(time%ticks_midday)))/ticks_midday; // scale to LED brightnesses for hours
187  uint32_t led_minute = (LED_WS2812B_LEDS*(256*(uint64_t)(time%ticks_hour)))/ticks_hour; // scale to LED brightnesses for minutes
188  if (led_hour>=LED_WS2812B_LEDS*256 || led_minute>=LED_WS2812B_LEDS*256) { // a calculation error occurred
189  return;
190  }
191  // show hours and minutes on LEDs
192  if (led_hour>led_minute) {
193  // show hours in blue (and clear other LEDs)
194  for (uint16_t led=0; led<LED_WS2812B_LEDS; led++) {
195  clock_leds[led*3+0] = 0;
196  clock_leds[led*3+1] = 0;
197  if (led_hour>=0xff) { // full hours
198  clock_leds[led*3+2] = 0xff;
199  } else { // running hours
200  clock_leds[led*3+2] = led_hour;
201  }
202  led_hour -= clock_leds[led*3+2];
203  }
204  // show minutes in green (override hours)
205  for (uint16_t led=0; led<LED_WS2812B_LEDS && led_minute>0; led++) {
206  clock_leds[led*3+0] = 0;
207  if (led_minute>=0xff) { // full minutes
208  clock_leds[led*3+1] = 0xff;
209  } else { // running minutes
210  clock_leds[led*3+1] = led_minute;
211  }
212  led_minute -= clock_leds[led*3+1];
213  clock_leds[led*3+2] = 0;
214  }
215  } else {
216  // show minutes in green (and clear other LEDs)
217  for (uint16_t led=0; led<LED_WS2812B_LEDS; led++) {
218  clock_leds[led*3+0] = 0;
219  if (led_minute>=0xff) { // full minutes
220  clock_leds[led*3+1] = 0xff;
221  } else { // running minutes
222  clock_leds[led*3+1] = led_minute;
223  }
224  led_minute -= clock_leds[led*3+1];
225  clock_leds[led*3+2] = 0;
226  }
227  // show hours in blue (override minutes)
228  for (uint16_t led=0; led<LED_WS2812B_LEDS && led_hour>0; led++) {
229  clock_leds[led*3+0] = 0;
230  clock_leds[led*3+1] = 0;
231  if (led_hour>=0xff) { // full hours
232  clock_leds[led*3+2] = 0xff;
233  } else { // running hours
234  clock_leds[led*3+2] = led_hour;
235  }
236  led_hour -= clock_leds[led*3+2];
237  }
238  }
239  // don't show seconds on full minute (better for first time setting, barely visible else)
240  if (time%ticks_minute==0) {
241  return;
242  }
243  uint32_t led_second = (LED_WS2812B_LEDS*(256*(uint64_t)(time%ticks_minute)))/ticks_minute; // scale to LED brightnesses for seconds
244  uint8_t brightness_second = led_second%256; // get brightness for seconds for last LED
245  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)
246  // set seconds LED
247  clock_leds[second_led*3+0] = brightness_second;
248  //clock_leds[second_led*3+1] = 0; // clear other colors (minutes/hours indication)
249  //clock_leds[second_led*3+2] = 0; // clear other colors (minutes/hours indication)
250  // set previous seconds LED
251  second_led = ((second_led==0) ? LED_WS2812B_LEDS-1 : second_led-1); // previous LED
252  clock_leds[second_led*3+0] = 0xff-brightness_second;
253  //clock_leds[second_led*3+1] = 0; // clear other colors (minutes/hours indication)
254  //clock_leds[second_led*3+2] = 0; // clear other colors (minutes/hours indication)
255 }
256 
261 static void clock_leds_set(void)
262 {
263  for (uint16_t i=0; i<LENGTH(clock_leds)/3; i++) {
264  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)
265  }
266 }
267 
271 static void clock_set_time(uint32_t time)
272 {
273  clock_show_time(time); // set time
274  clock_leds_set(); // set the colors of all LEDs
275  led_ws2812b_transmit(); // transmit set color
276 }
277 
282 static void clock_animate_time(uint32_t time)
283 {
284  static uint32_t display_time = 0; // the time to display
285  while (display_time<time) {
286  if (display_time+ticks_hour<=time) { // first set hours
287  display_time += ticks_hour; // increment hours
288  } else if (display_time+ticks_minute<=time) { // second set minutes
289  display_time += ticks_minute; // increment minutes
290  } else if (display_time+ticks_second<=time) { // third set seconds
291  display_time += ticks_second; // increment seconds
292  } else { // finally set time
293  display_time = time;
294  }
295  clock_set_time(display_time); // set time (progress)
296  // delay some time for the animation
297  for (uint32_t i=0; i<400000; i++) {
298  __asm__("nop");
299  }
300  }
301 }
302 
305 static void clock_hours(void)
306 {
307  for (uint16_t i=0; i<512; i++) { // fade in and out
308  uint8_t brightness = (i>255 ? 512-i-1 : i); // get fade brightness
309  for (uint8_t hour=0; hour<12; hour++) { // set all hour colors
310  uint16_t led = LED_WS2812B_LEDS/12*hour; // get LED four hour mark
311  clock_leds[led*3+0] = brightness; // set brightness
312  clock_leds[led*3+1] = brightness; // set brightness
313  clock_leds[led*3+2] = brightness; // set brightness
314  }
315  clock_leds_set(); // set the colors of all LEDs
316  led_ws2812b_transmit(); // transmit set color
317  // delay some time for the animation
318  for (uint32_t j=0; j<40000; j++) {
319  __asm__("nop");
320  }
321  }
322 }
323 
327 static void process_command(char* str)
328 {
329  // split command
330  const char* delimiter = " ";
331  char* word = strtok(str,delimiter);
332  if (!word) {
333  goto error;
334  }
335  // parse command
336  if (0==strcmp(word,"help")) {
337  printf("available commands:\n");
338  printf("DCF77 on|off\n");
339  printf("time [HH:MM:SS]\n");
340 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
341  printf("date [YYYY-MM-DD]\n");
342 #endif
343  } else if (0==strcmp(word,"DCF77")) {
344  word = strtok(NULL,delimiter);
345  if (!word) {
346  goto error;
347  } else if (0==strcmp(word,"on")) {
348  rtc_dcf77_on(); // switch DCF77 on
349  printf("DCF77 receiver switched on\n"); // notify user
350  } else if (0==strcmp(word,"off")) {
351  rtc_dcf77_off(); // switch DCF77 off
352  printf("DCF77 receiver switched off\n"); // notify user
353  } else {
354  goto error;
355  }
356  } else if (0==strcmp(word,"time")) {
357  word = strtok(NULL,delimiter);
358  if (!word) {
359 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
360  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
361 #else
362  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
363 #endif
364  } 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
365  goto error;
366  } else {
367 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
368  if (!rtc_ds1307_write_hours((word[0]-'0')*10+(word[1]-'0')*1)) {
369  printf("setting hours failed\n");
370  } else if (!rtc_ds1307_write_minutes((word[3]-'0')*10+(word[4]-'0')*1)) {
371  printf("setting minutes failed\n");
372  } else if (!rtc_ds1307_write_seconds((word[6]-'0')*10+(word[7]-'0')*1)) {
373  printf("setting seconds failed\n");
374  } else {
376  rtc_ds1307_oscillator_enable(); // be sure the oscillation is enabled
377  printf("time set\n");
378  }
379 #else
380  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
381  printf("time set\n");
382 #endif
383  }
384 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
385  } else if (0==strcmp(word,"date")) {
386  word = strtok(NULL,delimiter);
387  if (!word) {
388  printf("current date: 20%02u-%02u-%02u\n", rtc_ds1307_read_year(), rtc_ds1307_read_month(), rtc_ds1307_read_date());
389  } 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') {
390  goto error;
391  } else {
392  if (!rtc_ds1307_write_year((word[2]-'0')*10+(word[3]-'0')*1)) {
393  printf("setting year failed\n");
394  } else if (!rtc_ds1307_write_month((word[5]-'0')*10+(word[6]-'0')*1)) {
395  printf("setting month failed\n");
396  } else if (!rtc_ds1307_write_date((word[8]-'0')*10+(word[9]-'0')*1)) {
397  printf("setting day failed\n");
398  } else {
399  printf("date set\n");
400  }
401  }
402 #endif
403  } else {
404  goto error;
405  }
406 
407  return; // command successfully processed
408 error:
409  printf("command not recognized. enter help to list commands\n");
410 }
411 
415 int main(void)
416 {
417  rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
418 
419  // setup LED
420  rcc_periph_clock_enable(LED_RCC); // enable clock for LED
421  gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, LED_PIN); // set LED pin to 'output push-pull'
422  led_off(); // switch off LED per default
423 
424  // setup USART and USB for user communication
425  usart_setup(); // setup USART (for printing)
426  cdcacm_setup(); // setup USB CDC ACM (for printing)
427  setbuf(stdout, NULL); // set standard out buffer to NULL to immediately print
428  setbuf(stderr, NULL); // set standard error buffer to NULL to immediately print
429 
430  // minimal setup ready
431  printf("welcome to the CuVoodoo LED clock\n"); // print welcome message
432 
433  // setup button
434 #if defined(BUTTON_RCC) && defined(BUTTON_PORT) && defined(BUTTON_PIN) && defined(BUTTON_EXTI) && defined(BUTTON_IRQ)
435  rcc_periph_clock_enable(BUTTON_RCC); // enable clock for button
436  gpio_set_mode(BUTTON_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, BUTTON_PIN); // set button pin to input
437  gpio_clear(BUTTON_PORT, BUTTON_PIN); // pull down to be able to detect button push (go high)
438  rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
439  exti_select_source(BUTTON_EXTI, BUTTON_PORT); // mask external interrupt of this pin only for this port
440  exti_set_trigger(BUTTON_EXTI, EXTI_TRIGGER_RISING); // trigger when button is pressed
441  exti_enable_request(BUTTON_EXTI); // enable external interrupt
442  nvic_enable_irq(BUTTON_IRQ); // enable interrupt
443 #endif
444 
445  // setup RTC
446 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
447  printf("setup external RTC: ");
448  rtc_ds1307_setup(); // setup external RTC module
449 #else
450  printf("setup internal RTC: ");
451  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)
452  rtc_interrupt_enable(RTC_SEC); // enable RTC interrupt on "seconds"
453  nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt
454 #endif
455  printf("OK\n");
456 
457  // setup DCF77
458  printf("setup DCF77 receiver: ");
459  rtc_dcf77_setup(); // setup DCF77 module
460  printf("OK\n");
461  rtc_dcf77_on(); // switch DCF77 on to get correct time
462  printf("DCF77 receiver switched on\n"); // notify user
463 
464  // setup WS2812B LEDs
465  printf("setup LEDs: ");
466  for (uint16_t i=0; i<LENGTH(gamma_correction_lut); i++) { // generate gamma correction table
467  gamma_correction_lut[i] = powf((float)i / (float)LENGTH(gamma_correction_lut), 2.2)*LENGTH(gamma_correction_lut); // calculate using fixed gamma value
468  }
469  led_ws2812b_setup(); // setup WS2812B LEDs
470  clock_clear(); // clear all LEDs
471  clock_leds_set(); // set the colors of all LEDs
472  led_ws2812b_transmit(); // transmit set color
473  printf("OK\n");
474 
475  // setup ADC to photo-resistor voltage
476  printf("setup brightness sensor: ");
477  rcc_periph_clock_enable(PHOTORESISTOR_PORT_RCC); // enable clock for photo-resistor GPIO peripheral
478  gpio_set_mode(PHOTORESISTOR_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, PHOTORESISTOR_PIN); // set photo-resistor GPIO as analogue input for the ADC
479  rcc_periph_clock_enable(RCC_ADC1); // enable clock for ADC peripheral
480  adc_off(ADC1); // switch off ADC while configuring it
481  // configuration is correct per default
482  adc_set_single_conversion_mode(ADC1); // we just want one measurement
483  adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC); // use 28.5 cycles to sample (long enough to be stable)
484  adc_enable_temperature_sensor(ADC1); // enable internal voltage reference
485  adc_enable_discontinuous_mode_regular(ADC1, 1); // do only one conversion per sequence
486  adc_enable_external_trigger_regular(ADC1, ADC_CR2_EXTSEL_SWSTART); // use software trigger to start conversion
487  adc_power_on(ADC1); // switch on ADC
488  for (uint32_t i=0; i<800000; i++) { // wait t_stab for the ADC to stabilize
489  __asm__("nop");
490  }
491  adc_reset_calibration(ADC1); // remove previous non-calibration
492  adc_calibration(ADC1); // calibrate ADC for less accuracy errors
493  // read internal reference 1.2V
494  uint8_t channels[] = {ADC_CHANNEL17}; // voltages to convert
495  adc_set_regular_sequence(ADC1, LENGTH(channels), channels); // set channels to convert
496  adc_start_conversion_regular(ADC1); // start conversion to get first voltage of this group
497  while (!adc_eoc(ADC1)); // wait until conversion finished
498  uint16_t ref_value = adc_read_regular(ADC1); // read internal reference 1.2V voltage value
499  // now use interrupts to only measure ambient luminosity
500  channels[0] = PHOTORESISTOR_ADC_CHANNEL; // only measure ambient luminosity
501  adc_set_regular_sequence(ADC1, 1, channels); // set now group
502  adc_enable_eoc_interrupt(ADC1); // enable interrupt for end of conversion
503  nvic_enable_irq(NVIC_ADC1_2_IRQ); // enable ADC interrupts
504  printf("OK\n");
505 
506  // verify is external RTC is running
507 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
509  printf("/!\\ RTC oscillator is disabled: the battery may be empty\n");
510  }
511 #endif
512 
513  // get date and time
514  uint32_t ticks_time = 0;
515 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
516  uint8_t* rtc_ds1307_time = rtc_ds1307_read_time(); // get time/date from external RTC
517  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
518  ticks_time = rtc_ds1307_ticks; // save time
519  printf("current date: 20%02u-%02u-%02u\n", rtc_ds1307_time[6], rtc_ds1307_time[5], rtc_ds1307_time[4]);
520 #else
521  ticks_time = rtc_get_counter_val(); // get time/date from internal RTC
522 #endif
523  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
524  clock_animate_time(ticks_time); // set time with animation
525 
526  // main loop
527  printf("command input: ready\n");
528  bool action = false; // if an action has been performed don't go to sleep
529  button_flag = false; // reset button flag
530  char c = ' '; // to store received character
531  bool char_flag = false; // a new character has been received
532  while (true) { // infinite loop
533  while (usart_received) { // data received over UART
534  action = true; // action has been performed
535  led_toggle(); // toggle LED
536  c = usart_getchar(); // store receive character
537  char_flag = true; // notify character has been received
538  }
539  while (cdcacm_received) { // data received over USB
540  action = true; // action has been performed
541  led_toggle(); // toggle LED
542  c = cdcacm_getchar(); // store receive character
543  char_flag = true; // notify character has been received
544  }
545  while (char_flag) { // user data received
546  char_flag = false; // reset flag
547  action = true; // action has been performed
548  printf("%c",c); // echo receive character
549  if (c=='\r' || c=='\n') { // end of command received
550  if (command_i>0) { // there is a command to process
551  command[command_i] = 0; // end string
552  command_i = 0; // prepare for next command
553  process_command(command); // process user command
554  }
555  } else { // user command input
556  command[command_i] = c; // save command input
557  if (command_i<LENGTH(command)-2) { // verify if there is place to save next character
558  command_i++; // save next character
559  }
560  }
561  }
562  while (button_flag) { // user pressed button
563  action = true; // action has been performed
564  printf("time incremented by 1 second\n");
565  rtc_set_counter_val(rtc_get_counter_val()+ticks_second); // increment time
566  //led_toggle(); // toggle LED
567  for (uint32_t i=0; i<1000000; i++) { // wait a bit to remove noise and double trigger
568  __asm__("nop");
569  }
570  button_flag = false; // reset flag
571  }
572  while (rtc_dcf77_time_flag) { // the DCF77 module received a new time
573  rtc_dcf77_time_flag = false; // reset flag
574  action = true; // action has been performed
575  uint8_t* dcf77_time = rtc_dcf77_time(); // get time
576  if (dcf77_time) { // ensure it's valid
577  ticks_time = dcf77_time[1]*ticks_hour+dcf77_time[0]*ticks_minute; // calculate new time
578 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
579  rtc_ds1307_ticks = ticks_time; // set new time
580  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
581  rtc_ds1307_oscillator_enable(); // be sure the oscillation is enabled
582 #else
583  rtc_set_counter_val(ticks_time); // set new time to internal RTC
584 #endif
585  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
586  rtc_dcf77_off(); // switch DCF77 off since we have correct time
587  printf("DCF77 receiver switched off\n"); // notify user
588  } else {
589  printf("DCF77 time: error\n");
590  }
591  }
592 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
593  while (rtc_ds1307_tick_flag) { // the RTC tick for our counter passed
594  rtc_ds1307_tick_flag = false; // reset flag
595  ticks_time = rtc_ds1307_ticks; // copy time incremented by external RTC for processing
596 #else
597  while (rtc_internal_tick_flag) { // the internal RTC ticked
598  rtc_internal_tick_flag = false; // reset flag
599  ticks_time = rtc_get_counter_val(); // copy time from internal RTC for processing
600 #endif
601  action = true; // action has been performed
602  if ((ticks_time%(ticks_second/10))==0) { // one tenth of a second passed
603  adc_start_conversion_regular(ADC1); // start measuring ambient luminosity
604  }
605  if ((ticks_time%ticks_second)==0) { // one second passed
606  led_toggle(); // LED toggling confuses the 32.768 kHz oscillator on the blue pill
607  }
608  if ((ticks_time%ticks_minute)==0) { // one minute passed
609  printf("%02lu:%02lu:%02lu\n", ticks_time/ticks_hour, (ticks_time%ticks_hour)/ticks_minute, (ticks_time%ticks_minute)/ticks_second); // display external time
610  }
611  if ((ticks_time%ticks_hour)==0) { // one hours passed
612  clock_hours(); // show hour markers
613  rtc_dcf77_on(); // switch DCF77 on to update/correct time
614  printf("DCF77 receiver switched on\n"); // notify user
615  }
616  if (ticks_time>=ticks_midday*2) { // one day passed
617 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
618  rtc_ds1307_ticks = rtc_ds1307_ticks%ticks_midday; // reset time counter
619 #else
620  rtc_set_counter_val(rtc_get_counter_val()%ticks_midday); // reset time counter
621 #endif
622  }
623  clock_set_time(ticks_time); // set time
624  }
625  while (photoresistor_flag) { // new photo-resistor value has been measured
626  photoresistor_flag = false; // reset flag
627  action = true; // action has been performed
628  float photoresistor_voltage = photoresistor_value*1.2/ref_value; // calculate voltage from value
629  float new_clock_brightness = 0; // to calculate new brightness
630  if (photoresistor_voltage<PHOTORESISTOR_MAX) { // high ambient luminosity
631  new_clock_brightness = BRIGHTNESS_MAX; // set highest brightness
632  } else if (photoresistor_voltage>PHOTORESISTOR_MIN) { // low ambient luminosity
633  new_clock_brightness = BRIGHTNESS_MIN; // set low brightness
634  } else { // intermediate ambient luminosity
635  new_clock_brightness = BRIGHTNESS_MIN+(BRIGHTNESS_MAX-BRIGHTNESS_MIN)*(1-(photoresistor_voltage-PHOTORESISTOR_MAX)/(PHOTORESISTOR_MIN-PHOTORESISTOR_MAX)); // set variable brightness
636  }
637  clock_brightness = clock_brightness*(1-BRIGHTNESS_FACTOR)+new_clock_brightness*BRIGHTNESS_FACTOR; // calculate new brightness based on factor
638  //printf("photo-resistor voltage: %f, clock brightness: %f\n", photoresistor_voltage, clock_brightness);
639  }
640  if (action) { // go to sleep if nothing had to be done, else recheck for activity
641  action = false;
642  } else {
643  __WFI(); // go to sleep
644  }
645  }
646 
647  return 0;
648 }
649 
650 #if defined(BUTTON_ISR) && defined(BUTTON_EXTI)
651 
652 void BUTTON_ISR(void)
653 {
654  exti_reset_request(BUTTON_EXTI); // reset interrupt
655  button_flag = true; // perform button action
656 }
657 #endif
658 
660 void adc1_2_isr(void)
661 {
662  photoresistor_value = adc_read_regular(ADC1); // read measured photo-resistor value (clears interrupt flag)
663  photoresistor_flag = true; // notify new ambient luminosity has been measured
664 }
665 
666 #if defined(EXTERNAL_RTC) && EXTERNAL_RTC
667 #else
668 
669 void rtc_isr(void)
670 {
671  rtc_clear_flag(RTC_SEC); // clear flag
672  rtc_internal_tick_flag = true; // notify to show new time
673 }
674 #endif
675 
void led_ws2812b_setup(void)
setup WS2812B LED driver
Definition: led_ws2812b.c:94
float clock_brightness
factor to dim LED of the clock, depending on the ambient luminosity
Definition: main.c:108
bool rtc_ds1307_write_date(uint8_t date)
write date into RTC IC
Definition: rtc_ds1307.c:370
static void clock_clear(void)
switch off all clock LEDs
Definition: main.c:171
const uint32_t ticks_second
number of ticks in one second
Definition: main.c:74
void rtc_ds1307_setup(void)
setup communication with RTC IC configure the I2C port defined in the sources
Definition: rtc_ds1307.c:44
bool rtc_ds1307_write_time(uint8_t seconds, uint8_t minutes, uint8_t hours, uint8_t day, uint8_t date, uint8_t month, uint8_t year)
write time into RTC IC
Definition: rtc_ds1307.c:400
bool led_ws2812b_transmit(void)
transmit color values to WS2812B LEDs
Definition: led_ws2812b.c:76
void cdcacm_putchar(char c)
send character over USB (non-blocking)
Definition: usb_cdcacm.c:391
char command[32]
user input command
Definition: main.c:96
bool rtc_ds1307_write_year(uint8_t year)
write year into RTC IC
Definition: rtc_ds1307.c:390
const uint32_t ticks_midday
number of ticks in one midday (12 hours)
Definition: main.c:80
void led_off(void)
switch off board LED
Definition: global.h:73
bool rtc_ds1307_oscillator_enable(void)
enable RTC IC oscillator
Definition: rtc_ds1307.c:293
#define PHOTORESISTOR_ADC_CHANNEL
ADC channel.
Definition: main.c:86
bool rtc_ds1307_write_month(uint8_t month)
write month into RTC IC
Definition: rtc_ds1307.c:380
bool rtc_ds1307_write_minutes(uint8_t minutes)
write minutes into RTC IC
Definition: rtc_ds1307.c:340
static void clock_set_time(uint32_t time)
set the time on the LEDs
Definition: main.c:271
volatile bool rtc_ds1307_tick_flag
set on SQUARE_WAVE_TICS square wave ticks
Definition: rtc_ds1307.c:41
volatile bool rtc_internal_tick_flag
flag set when internal RTC ticked
Definition: main.c:58
void rtc_dcf77_setup(void)
setup DCF77 time receiver module
Definition: rtc_dcf77.c:40
static void process_command(char *str)
process user command
Definition: main.c:327
#define BRIGHTNESS_MIN
minimum LED brightness
Definition: main.c:110
char * b2s(uint64_t binary, uint8_t rjust)
get binary representation of a number
Definition: main.c:146
static void clock_show_time(uint32_t time)
show time on LED clock
Definition: main.c:184
static void clock_hours(void)
show animation with fading hours mark on clock LEDs
Definition: main.c:305
global definitions and methods
uint8_t clock_leds[LED_WS2812B_LEDS *3]
RGB values for the WS2812B clock LEDs.
Definition: main.c:94
volatile uint8_t usart_received
how many bytes available in the received buffer since last read
Definition: usart.c:58
#define PHOTORESISTOR_MIN
photo-resistor voltage for the minimum brightness
Definition: main.c:104
void rtc_dcf77_off(void)
switch off DCF77 time receiver module
Definition: rtc_dcf77.c:69
volatile uint16_t photoresistor_value
photo-resistor measurement of ambient luminosity
Definition: main.c:102
void cdcacm_setup(void)
setup USB CDC ACM peripheral
Definition: usb_cdcacm.c:357
uint8_t rtc_ds1307_read_month(void)
read month from RTC IC
Definition: rtc_ds1307.c:207
void adc1_2_isr(void)
interrupt service routine called when ADC conversion completed
Definition: main.c:660
void led_toggle(void)
toggle board LED
Definition: global.h:82
#define PHOTORESISTOR_PIN
pin of the port on which the battery is connected
Definition: main.c:89
uint8_t rtc_ds1307_read_year(void)
read year from RTC IC
Definition: rtc_ds1307.c:216
uint8_t * rtc_ds1307_read_time(void)
read time from RTC IC
Definition: rtc_ds1307.c:224
uint8_t rtc_ds1307_read_seconds(void)
read seconds from RTC IC
Definition: rtc_ds1307.c:154
uint8_t gamma_correction_lut[256]
gamma correction lookup table (common for all colors)
Definition: main.c:100
volatile uint8_t cdcacm_received
how many bytes available in the received buffer since last read
Definition: usb_cdcacm.c:210
static void clock_leds_set(void)
set the LEDs
Definition: main.c:261
uint8_t command_i
user input command index
Definition: main.c:98
bool rtc_ds1307_write_hours(uint8_t hours)
write hours into RTC IC
Definition: rtc_ds1307.c:350
volatile bool rtc_dcf77_time_flag
set when time information has been received
Definition: rtc_dcf77.c:37
void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue)
set color of a single LED
Definition: led_ws2812b.c:50
uint8_t rtc_ds1307_read_date(void)
read date from RTC IC
Definition: rtc_ds1307.c:198
bool rtc_ds1307_write_seconds(uint8_t seconds)
write seconds into RTC IC
Definition: rtc_ds1307.c:326
void rtc_isr(void)
interrupt service routine called when tick passed on RTC
Definition: main.c:669
void usart_putchar_nonblocking(char c)
send character over USART (non-blocking)
Definition: usart.c:117
#define BRIGHTNESS_MAX
maximum LED brightness
Definition: main.c:112
library for USART communication (API)
volatile bool photoresistor_flag
flag set when ambient luminosity is measured
Definition: main.c:55
#define BRIGHTNESS_FACTOR
the factor to change the brightness
Definition: main.c:114
#define LED_WS2812B_LEDS
number of LEDs on the WS2812B strip
Definition: led_ws2812b.h:24
#define PHOTORESISTOR_MAX
photo-resistor voltage for the maximum brightness
Definition: main.c:106
char usart_getchar(void)
get character received over USART (blocking)
Definition: usart.c:103
volatile uint32_t rtc_ds1307_ticks
increment on SQUARE_WAVE_TICS square wave ticks
Definition: rtc_ds1307.c:40
library for USB CDC ACM communication (API)
uint8_t * rtc_dcf77_time(void)
get last received DCF77 time
Definition: rtc_dcf77.c:74
volatile bool button_flag
flag set when board user button has been pressed/released
Definition: main.c:54
#define TICKS_PER_SECOND
the number of ticks in one second (32768 divisor greater than 256*LED_WS2812B_LEDS/60) ...
Definition: main.c:71
#define LENGTH(x)
get the length of an array
Definition: global.h:26
int _write(int file, char *ptr, int len)
default printf output
Definition: main.c:116
library to get time from a DCF77 module (API)
static void clock_animate_time(uint32_t time)
incrementally set the time on the LEDs
Definition: main.c:282
#define PHOTORESISTOR_PORT
port on which the battery is connected
Definition: main.c:87
bool rtc_ds1307_oscillator_disabled(void)
verify if oscillator is disabled
Definition: rtc_ds1307.c:133
library to communicate with the Maxim DS1307 I2C RTC IC (API)
int main(void)
program entry point this is the firmware function started by the micro-controller ...
Definition: main.c:415
void usart_setup(void)
setup USART peripheral
Definition: usart.c:60
uint8_t rtc_ds1307_read_hours(void)
read hours from RTC IC
Definition: rtc_ds1307.c:172
#define PHOTORESISTOR_PORT_RCC
timer port peripheral clock
Definition: main.c:88
void rtc_dcf77_on(void)
switch on DCF77 time receiver module
Definition: rtc_dcf77.c:64
library to drive a WS2812B LED chain (API)
char cdcacm_getchar(void)
get character received over USB (blocking)
Definition: usb_cdcacm.c:379
const uint32_t ticks_hour
number of ticks in one hour
Definition: main.c:78
uint8_t rtc_ds1307_read_minutes(void)
read minutes from RTC IC
Definition: rtc_ds1307.c:163
const uint32_t ticks_minute
number of ticks in one minute
Definition: main.c:76