CuVoodoo STM32F1 firmware template
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 <stdbool.h> // boolean type
24 #include <string.h> // string utilities
25 
26 /* STM32 (including CM3) libraries */
27 #include <libopencmsis/core_cm3.h> // Cortex M3 utilities
28 #include <libopencm3/cm3/scb.h> // vector table definition
29 #include <libopencm3/cm3/nvic.h> // interrupt utilities
30 #include <libopencm3/stm32/gpio.h> // general purpose input output library
31 #include <libopencm3/stm32/rcc.h> // real-time control clock library
32 #include <libopencm3/stm32/exti.h> // external interrupt utilities
33 #include <libopencm3/stm32/rtc.h> // real time clock utilities
34 #include <libopencm3/stm32/iwdg.h> // independent watchdog utilities
35 #include <libopencm3/stm32/dbgmcu.h> // debug utilities
36 #include <libopencm3/stm32/flash.h> // flash utilities
37 #include <libopencm3/stm32/timer.h> // timer utilities
38 
39 /* own libraries */
40 #include "global.h" // board definitions
41 #include "print.h" // printing utilities
42 #include "usart.h" // USART utilities
43 #include "usb_cdcacm.h" // USB CDC ACM utilities
44 #include "rtc_ds1307.h" // DS1307 RTC utilities
45 #include "led_tm1637.h" // TM1637 7-segment controller utilities
46 #include "led_max7219.h" // MAX7219 7-segment controller utilities
47 
48 #define WATCHDOG_PERIOD 10000
53 volatile bool rtc_tick_flag = false;
54 volatile bool frame_flag = false;
55 volatile bool keep_alive_flag = false;
56 volatile bool clap_flag = false;
59 #define POWER_SWITCH_PORT B
60 #define POWER_SWITCH_PIN 8
61 #define POWER_BUTTON_PORT B
62 #define POWER_BUTTON_PIN 1
64 #define SQUARE_WAVE_PORT B
65 #define SQUARE_WAVE_PIN 0
66 volatile uint8_t rtc_seconds = 0;
68 #define STANDBY_TIMEOUT 30
69 volatile uint16_t standby_timer = 0;
71 #define FRAME_TIMER 2
72 #define FRAME_RATE 25
73 volatile uint8_t frame_count = 0;
75 #define BUZZER_TIMER 1
76 #define BUZZER_1_PORT A
77 #define BUZZER_1_PIN 7
78 #define BUZZER_2_PORT A
79 #define BUZZER_2_PIN 8
81 #define MUX_EN_PORT B
82 #define MUX_EN_PIN 9
83 #define MUX_S0_PORT B
84 #define MUX_S0_PIN 3
85 #define MUX_S1_PORT B
86 #define MUX_S1_PIN 4
87 #define MUX_S2_PORT B
88 #define MUX_S2_PIN 5
91 struct number_t {
92  uint16_t number;
93  uint8_t up;
94  uint8_t down;
95  uint8_t display;
96 };
98 struct number_t numbers[] = {
99  {0, 0, 4, 4}, // episode
100  {0, 8, 12, 5}, // scene
101  {0, 1, 5, 6}, // take
102  {0, 2, 6, 3}, // video 1
103  {0, 10, 14, 2}, // audio 1
104  {0, 3, 7, 1}, // video 2
105  {0, 11, 15, 0}, // audio 2
106 };
107 #define BUTTON_SECONDS_UP 9
108 #define BUTTON_SECONDS_DOWN 13
110 // buttons to adjust numbers and seconds (mutliplexed in a 4x4 matrix)
111 #define BUTTONS_DRIVE_PORT A
112 #define BUTTONS_DRIVE_PIN0 0
113 #define BUTTONS_DRIVE_PIN1 1
114 #define BUTTONS_DRIVE_PIN2 2
115 #define BUTTONS_DRIVE_PIN3 3
116 #define BUTTONS_READ_PORT A
117 #define BUTTONS_READ_PIN0 4
118 #define BUTTONS_READ_PIN1 5
119 #define BUTTONS_READ_PIN2 6
120 #define BUTTONS_READ_PIN3 15
122 #define CLAP_BUTTON_PORT B
123 #define CLAP_BUTTON_PIN 14
126 #define MORSE_DOT 1
127 uint8_t morse[2*4*5*2] = {0};
128 uint8_t morse_i = 0;
129 int16_t morse_delay = -1;
132 static char command[32] = {0};
133 
134 uint8_t command_i = 0;
135 
136 size_t putc(char c)
137 {
138  size_t length = 0; // number of characters printed
139  static char newline = 0; // to remember on which character we sent the newline
140  if (0==c) {
141  length = 0; // don't print string termination character
142  } else if ('\r' == c || '\n' == c) { // send CR+LF newline for most carriage return and line feed combination
143  if (0==newline || c==newline) { // send newline only if not already send (and only once on \r\n or \n\r)
144  usart_putchar_nonblocking('\r'); // send CR over USART
145  cdcacm_putchar('\r'); // send CR over USB
146  usart_putchar_nonblocking('\n'); // send LF over USART
147  cdcacm_putchar('\n'); // send LF over USB
148  length += 2; // remember we printed 2 characters
149  newline = c; // remember on which character we sent the newline
150  } else {
151  length = 0; // the \r or \n of \n\r or \r\n has already been printed
152  }
153  } else {
154  usart_putchar_nonblocking(c); // send byte over USART
155  cdcacm_putchar(c); // send byte over USB
156  newline = 0; // clear new line
157  length++; // remember we printed 1 character
158  }
159  return length; // return number of characters printed
160 }
161 
165 static void process_command(char* str)
166 {
167  // split command
168  const char* delimiter = " ";
169  char* word = strtok(str,delimiter);
170  if (!word) {
171  goto error;
172  }
173  // parse command
174  if (0==strcmp(word,"help")) {
175  printf("available commands:\n");
176  printf("led [on|off|toggle]\n");
177  printf("time [HH:MM:SS]\n");
178  printf("date [YYYY-MM-DD]\n");
179  } else if (0==strcmp(word,"led")) {
180  word = strtok(NULL,delimiter);
181  if (!word) {
182  goto error;
183  } else if (0==strcmp(word,"on")) {
184  led_on(); // switch LED on
185  printf("LED switched on\n"); // notify user
186  } else if (0==strcmp(word,"off")) {
187  led_off(); // switch LED off
188  printf("LED switched off\n"); // notify user
189  } else if (0==strcmp(word,"toggle")) {
190  led_toggle(); // toggle LED
191  printf("LED toggled\n"); // notify user
192  } else {
193  goto error;
194  }
195  } else if (0==strcmp(word,"time")) {
196  word = strtok(NULL,delimiter);
197  if (!word) {
198  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
199  } 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
200  goto error;
201  } else {
202  if (!rtc_ds1307_write_hours((word[0]-'0')*10+(word[1]-'0')*1)) {
203  printf("setting hours failed\n");
204  } else if (!rtc_ds1307_write_minutes((word[3]-'0')*10+(word[4]-'0')*1)) {
205  printf("setting minutes failed\n");
206  } else if (!rtc_ds1307_write_seconds((word[6]-'0')*10+(word[7]-'0')*1)) {
207  printf("setting seconds failed\n");
208  } else {
209  rtc_ds1307_oscillator_enable(); // be sure the oscillation is enabled
210  printf("time set\n");
211  }
212  }
213  } else if (0==strcmp(word,"date")) {
214  word = strtok(NULL,delimiter);
215  if (!word) {
216  printf("current date: 20%02u-%02u-%02u\n", rtc_ds1307_read_year(), rtc_ds1307_read_month(), rtc_ds1307_read_date());
217  } 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') {
218  goto error;
219  } else {
220  if (!rtc_ds1307_write_year((word[2]-'0')*10+(word[3]-'0')*1)) {
221  printf("setting year failed\n");
222  } else if (!rtc_ds1307_write_month((word[5]-'0')*10+(word[6]-'0')*1)) {
223  printf("setting month failed\n");
224  } else if (!rtc_ds1307_write_date((word[8]-'0')*10+(word[9]-'0')*1)) {
225  printf("setting day failed\n");
226  } else {
227  printf("date set\n");
228  }
229  }
230  } else {
231  goto error;
232  }
233 
234  return; // command successfully processed
235 error:
236  printf("command not recognized. enter help to list commands\n");
237  return;
238 }
239 
243 static void mux_select(uint8_t output)
244 {
245  if (output>7) { // multiplexer is only controlling 8 outputs
246  return;
247  }
248  gpio_clear(GPIO(MUX_EN_PORT), GPIO(MUX_EN_PIN)); // enable multiplexer
249  switch (output) {
250  case 0: // output on channel C0
251  gpio_clear(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN));
252  gpio_clear(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN));
253  gpio_clear(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN));
254  break;
255  case 1: // output on channel C1
256  gpio_set(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN));
257  gpio_clear(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN));
258  gpio_clear(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN));
259  break;
260  case 2: // output on channel C2
261  gpio_clear(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN));
262  gpio_set(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN));
263  gpio_clear(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN));
264  break;
265  case 3: // output on channel C3
266  gpio_set(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN));
267  gpio_set(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN));
268  gpio_clear(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN));
269  break;
270  case 4: // output on channel C4
271  gpio_clear(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN));
272  gpio_clear(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN));
273  gpio_set(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN));
274  break;
275  case 5: // output on channel C5
276  gpio_set(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN));
277  gpio_clear(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN));
278  gpio_set(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN));
279  break;
280  case 6: // output on channel C6
281  gpio_clear(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN));
282  gpio_set(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN));
283  gpio_set(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN));
284  break;
285  case 7: // output on channel C7
286  gpio_set(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN));
287  gpio_set(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN));
288  gpio_set(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN));
289  break;
290  default:
291  break;
292  }
293 }
294 
296 static void encode_morse(void)
297 {
298  // numbers (scene and take) to encode in Morse code
299  uint16_t morse_numbers[] = {numbers[1].number, numbers[2].number};
300  printf("morsing scene: %u, take: %u\n", morse_numbers[0], morse_numbers[1]);
301  for (uint8_t morse_number=0; morse_number<LENGTH(morse_numbers); morse_number++) {
302  bool not_zero = false; // has the first non-zero digit been found
303  if (0xffff==morse_numbers[morse_number]) { // number disabled, don't beep
304  for (uint8_t i=morse_number*4*2*5; i<(morse_number+1)*4*2*5; i++) {
305  morse[i] = 0; // no beeping
306  }
307  } else { // encode number in Morse code
308  for (uint8_t digit=0; digit<4; digit++) { // encode each digit
309  uint16_t number = morse_numbers[morse_number]; // digit to encode
310  // get only the digit from the number
311  for (uint8_t divide=digit; divide<3; divide++) {
312  number /= 10;
313  }
314  number %= 10;
315  // remember when we found the first non-zero digit
316  if (number!=0 || digit==3) {
317  not_zero = true;
318  }
319  // encode digit in Morse code (1=short, 3=long)
320  switch (number) {
321  case 1: // Morse code for 1: short, long, long, long, long
322  morse[(morse_number*4+digit)*2*5+0] = 1*MORSE_DOT; // short
323  morse[(morse_number*4+digit)*2*5+2] = 3*MORSE_DOT; // long
324  morse[(morse_number*4+digit)*2*5+4] = 3*MORSE_DOT; // long
325  morse[(morse_number*4+digit)*2*5+6] = 3*MORSE_DOT; // long
326  morse[(morse_number*4+digit)*2*5+8] = 3*MORSE_DOT; // long
327  break;
328  case 2: // Morse code for 2: short, short, long, long, long
329  morse[(morse_number*4+digit)*2*5+0] = 1*MORSE_DOT; // short
330  morse[(morse_number*4+digit)*2*5+2] = 1*MORSE_DOT; // short
331  morse[(morse_number*4+digit)*2*5+4] = 3*MORSE_DOT; // long
332  morse[(morse_number*4+digit)*2*5+6] = 3*MORSE_DOT; // long
333  morse[(morse_number*4+digit)*2*5+8] = 3*MORSE_DOT; // long
334  break;
335  case 3: // Morse code for 3: short, short, short, long, long
336  morse[(morse_number*4+digit)*2*5+0] = 1*MORSE_DOT; // short
337  morse[(morse_number*4+digit)*2*5+2] = 1*MORSE_DOT; // short
338  morse[(morse_number*4+digit)*2*5+4] = 1*MORSE_DOT; // short
339  morse[(morse_number*4+digit)*2*5+6] = 3*MORSE_DOT; // long
340  morse[(morse_number*4+digit)*2*5+8] = 3*MORSE_DOT; // long
341  break;
342  case 4: // Morse code for 4: short, short, short, short, long
343  morse[(morse_number*4+digit)*2*5+0] = 1*MORSE_DOT; // short
344  morse[(morse_number*4+digit)*2*5+2] = 1*MORSE_DOT; // short
345  morse[(morse_number*4+digit)*2*5+4] = 1*MORSE_DOT; // short
346  morse[(morse_number*4+digit)*2*5+6] = 1*MORSE_DOT; // short
347  morse[(morse_number*4+digit)*2*5+8] = 3*MORSE_DOT; // long
348  break;
349  case 5: // Morse code for 5: short, short, short, short, short
350  morse[(morse_number*4+digit)*2*5+0] = 1*MORSE_DOT; // short
351  morse[(morse_number*4+digit)*2*5+2] = 1*MORSE_DOT; // short
352  morse[(morse_number*4+digit)*2*5+4] = 1*MORSE_DOT; // short
353  morse[(morse_number*4+digit)*2*5+6] = 1*MORSE_DOT; // short
354  morse[(morse_number*4+digit)*2*5+8] = 1*MORSE_DOT; // short
355  break;
356  case 6: // Morse code for 6: long, short, short, short, short
357  morse[(morse_number*4+digit)*2*5+0] = 3*MORSE_DOT; // long
358  morse[(morse_number*4+digit)*2*5+2] = 1*MORSE_DOT; // short
359  morse[(morse_number*4+digit)*2*5+4] = 1*MORSE_DOT; // short
360  morse[(morse_number*4+digit)*2*5+6] = 1*MORSE_DOT; // short
361  morse[(morse_number*4+digit)*2*5+8] = 1*MORSE_DOT; // short
362  break;
363  case 7: // Morse code for 7: long, long, short, short, short
364  morse[(morse_number*4+digit)*2*5+0] = 3*MORSE_DOT; // long
365  morse[(morse_number*4+digit)*2*5+2] = 3*MORSE_DOT; // long
366  morse[(morse_number*4+digit)*2*5+4] = 1*MORSE_DOT; // short
367  morse[(morse_number*4+digit)*2*5+6] = 1*MORSE_DOT; // short
368  morse[(morse_number*4+digit)*2*5+8] = 1*MORSE_DOT; // short
369  break;
370  case 8: // Morse code for 8: long, long, long, short, short
371  morse[(morse_number*4+digit)*2*5+0] = 3*MORSE_DOT; // long
372  morse[(morse_number*4+digit)*2*5+2] = 3*MORSE_DOT; // long
373  morse[(morse_number*4+digit)*2*5+4] = 3*MORSE_DOT; // long
374  morse[(morse_number*4+digit)*2*5+6] = 1*MORSE_DOT; // short
375  morse[(morse_number*4+digit)*2*5+8] = 1*MORSE_DOT; // short
376  break;
377  case 9: // Morse code for 9: long, long, long, long, short
378  morse[(morse_number*4+digit)*2*5+0] = 3*MORSE_DOT; // long
379  morse[(morse_number*4+digit)*2*5+2] = 3*MORSE_DOT; // long
380  morse[(morse_number*4+digit)*2*5+4] = 3*MORSE_DOT; // long
381  morse[(morse_number*4+digit)*2*5+6] = 3*MORSE_DOT; // long
382  morse[(morse_number*4+digit)*2*5+8] = 1*MORSE_DOT; // short
383  break;
384  case 0: // Morse code for 0: long, long, long, long, long
385  if (not_zero) { // encode zero if they are not leading
386  morse[(morse_number*4+digit)*2*5+0] = 3*MORSE_DOT; // long
387  morse[(morse_number*4+digit)*2*5+2] = 3*MORSE_DOT; // long
388  morse[(morse_number*4+digit)*2*5+4] = 3*MORSE_DOT; // long
389  morse[(morse_number*4+digit)*2*5+6] = 3*MORSE_DOT; // long
390  morse[(morse_number*4+digit)*2*5+8] = 3*MORSE_DOT; // long
391  } else { // don't encode the first digits if they are zero
392  morse[(morse_number*4+digit)*2*5+0] = 0; // no beeping
393  morse[(morse_number*4+digit)*2*5+2] = 0; // no beeping
394  morse[(morse_number*4+digit)*2*5+4] = 0; // no beeping
395  morse[(morse_number*4+digit)*2*5+6] = 0; // no beeping
396  morse[(morse_number*4+digit)*2*5+8] = 0; // no beeping
397  }
398  break;
399  }
400  // fill the spaces between the dots
401  for (uint8_t space=0; space<5; space++) {
402  if (0==morse[(morse_number*4+digit)*2*5+space*2]) { // don't add space if there is no Morse code
403  morse[(morse_number*4+digit)*2*5+space*2+1] = 0; // no space
404  } else { // add space between Morse codes
405  morse[(morse_number*4+digit)*2*5+space*2+1] = 1*MORSE_DOT; // add space
406  }
407  }
408  }
409  }
410  }
411  morse[4*2*5+1] = 3*MORSE_DOT; // use longer silence between the two numbers
412  morse_i = 0; // reset Morse index
413 }
414 
418 void main(void);
419 void main(void)
420 {
421  rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
422 
423  // keep power on a soon as possible
424  rcc_periph_clock_enable(RCC_GPIO(POWER_SWITCH_PORT)); // enable clock for GPIO
425  gpio_set_mode(GPIO(POWER_SWITCH_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(POWER_SWITCH_PIN)); // set as output to control power
426  gpio_set(GPIO(POWER_SWITCH_PORT), GPIO(POWER_SWITCH_PIN)); // enable power by saturating nMOS controlling power
427  rcc_periph_clock_enable(RCC_GPIO(POWER_BUTTON_PORT)); // enable clock for GPIO domain
428  gpio_set_mode(GPIO(POWER_BUTTON_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(POWER_BUTTON_PIN)); // set pin as input to detect power switching activity
429  rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
430  exti_select_source(EXTI(POWER_BUTTON_PIN), GPIO(POWER_BUTTON_PORT)); // mask external interrupt of this pin only for this port
431  exti_set_trigger(EXTI(POWER_BUTTON_PIN), EXTI_TRIGGER_BOTH); // trigger on any activity of the power switch
432  exti_enable_request(EXTI(POWER_BUTTON_PIN)); // enable external interrupt
433  nvic_enable_irq(NVIC_EXTI_IRQ(POWER_BUTTON_PIN)); // enable interrupt
434 
435 #if DEBUG
436  // enable functionalities for easier debug
437  DBGMCU_CR |= DBGMCU_CR_IWDG_STOP; // stop independent watchdog counter when code is halted
438  DBGMCU_CR |= DBGMCU_CR_WWDG_STOP; // stop window watchdog counter when code is halted
439  DBGMCU_CR |= DBGMCU_CR_STANDBY; // allow debug also in standby mode (keep digital part and clock powered)
440  DBGMCU_CR |= DBGMCU_CR_STOP; // allow debug also in stop mode (keep clock powered)
441  DBGMCU_CR |= DBGMCU_CR_SLEEP; // allow debug also in sleep mode (keep clock powered)
442 #else
443  // setup watchdog to reset in case we get stuck (i.e. when an error occurred)
444  iwdg_set_period_ms(WATCHDOG_PERIOD); // set independent watchdog period
445  iwdg_start(); // start independent watchdog
446 #endif
447 
448  board_setup(); // setup board
449  usart_setup(); // setup USART for user communication
450  cdcacm_setup(); // setup USB ACM (serial) for user communication
451  gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON,0); // disable JTAG (but leave SWD on) since we need most of the GPIOs
452  printf("\nwelcome to the CuVoodoo clapperboard\n"); // print welcome message
453 
454 #if !(DEBUG)
455  // show watchdog information
456  printf("watchdog set to (%2u.%2us)\n",WATCHDOG_PERIOD/1000, (WATCHDOG_PERIOD/10)%100);
457  if (FLASH_OBR&FLASH_OBR_OPTERR) {
458  printf("option bytes not set in flash: software wachtdog used (not started at reset)\n");
459  } else if (FLASH_OBR&FLASH_OBR_WDG_SW) {
460  printf("software wachtdog used (not started at reset)\n");
461  } else {
462  printf("hardware wachtdog used (started at reset)\n");
463  }
464 #endif
465 
466  // setup external RTC
467  printf("setup external RTC: ");
468  rtc_ds1307_setup(); // setup external RTC module
469  // enable square wave output and configure input interrupt to count seconds
470  rtc_ds1307_write_square_wave(1); // enable 1 Hz square wave output to sync on seconds
471  rcc_periph_clock_enable(RCC_GPIO(SQUARE_WAVE_PORT)); // enable clock for GPIO
472  gpio_set_mode(GPIO(SQUARE_WAVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(SQUARE_WAVE_PIN)); // set pin to input
473  rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
474  exti_select_source(EXTI(SQUARE_WAVE_PIN), GPIO(SQUARE_WAVE_PORT)); // mask external interrupt of this pin only for this port
475  exti_set_trigger(EXTI(SQUARE_WAVE_PIN), EXTI_TRIGGER_FALLING); // trigger on falling edge of square wave (this is also when the RTC register are updated)
476  exti_enable_request(EXTI(SQUARE_WAVE_PIN)); // enable external interrupt
477  nvic_enable_irq(NVIC_EXTI_IRQ(SQUARE_WAVE_PIN)); // enable interrupt
478  printf("OK\n");
479 
480  // verify if external RTC is running
482  printf("/!\\ RTC oscillator is disabled: the battery may be empty\n");
483  rtc_ds1307_oscillator_enable(); // enable oscillator again
484  }
485  // get date
486  uint8_t* rtc_ds1307_time = rtc_ds1307_read_time(); // get time/date from external RTC
487  if (rtc_ds1307_time==NULL) {
488  printf("could not get time from DS1307\n");
489  } else {
490  rtc_seconds = rtc_ds1307_time[0]; // remember seconds of minute
491  printf("current date: 20%02u-%02u-%02u %02u:%02u:%02u\n", rtc_ds1307_time[6], rtc_ds1307_time[5], rtc_ds1307_time[4], rtc_ds1307_time[2], rtc_ds1307_time[1], rtc_ds1307_time[0]);
492  }
493 
494  // setup analog multiplexer for TM1637 clock signal (mutliplex the clock signal we output between the 7 TM1637 displays)
495  printf("setup multiplexer: ");
496  rcc_periph_clock_enable(RCC_GPIO(MUX_EN_PORT)); // enable clock for GPIO port domain
497  gpio_set_mode(GPIO(MUX_EN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(MUX_EN_PIN)); // to enable/disable the multiplexer
498  gpio_set(GPIO(MUX_EN_PORT), GPIO(MUX_EN_PIN)); // disable multiplexer
499  rcc_periph_clock_enable(RCC_GPIO(MUX_S0_PORT)); // enable clock for GPIO port domain
500  gpio_set_mode(GPIO(MUX_S0_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(MUX_S0_PIN)); // to select output channel
501  rcc_periph_clock_enable(RCC_GPIO(MUX_S1_PORT)); // enable clock for GPIO port domain
502  gpio_set_mode(GPIO(MUX_S1_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(MUX_S1_PIN)); // to select output channel
503  rcc_periph_clock_enable(RCC_GPIO(MUX_S2_PORT)); // enable clock for GPIO port domain
504  gpio_set_mode(GPIO(MUX_S2_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(MUX_S2_PIN)); // to select output channel
505  printf("OK\n");
506 
507  // setup TM1637 4-digit 7-segment displays to display the numbers
508  printf("setup 7-segment displays: ");
509  led_tm1637_setup(); // setup TM1637
510  for (uint8_t tm1637=0; tm1637<LENGTH(numbers); tm1637++) {
511  mux_select(tm1637);
512  if (!led_tm1637_time(88,88)) { // test TM1637 display
513  printf("could not test TM1637 %u\n", tm1637);
514  }
515  }
516  // setup MAX7219 8-digit 7-segment displays to display date and time
517  led_max7219_setup(2); // setup MAX7219
518  led_max7219_intensity(15,8,0xff); // set brightness max and enable all digits
519  led_max7219_test(true,0xff); // test all MAX7219 displays
520  for (volatile uint32_t i=0; i<10000000; i++); // wait a bit to have the user check the displays
521  // switch displays back off
522  for (uint8_t tm1637=0; tm1637<LENGTH(numbers); tm1637++) {
523  mux_select(tm1637);
524  if (!led_tm1637_off()) { // switch off display
525  printf("could not switch off TM1637 %u\n", tm1637);
526  }
527  }
528  led_max7219_test(false,0xff); // go back in normal operation
529  led_max7219_off(0xff); // switch displays off
530  printf("OK\n");
531 
532  // get and display numbers on TM1637
533  for (uint8_t number=0; number<LENGTH(numbers); number++) {
534  uint8_t bytes[2] = {0}; // bytes for the numbers
535  if (!rtc_ds1307_read_rom(number*LENGTH(bytes), bytes, LENGTH(bytes))) { // get number from EEPROM
536  printf("could not get number from EEPROM\n");
537  }
538  numbers[number].number = (bytes[0]<<8)+bytes[1]; // save number
539  // display number
540  mux_select(numbers[number].display);
541  printf("number %u: ", number);
542  if (0xffff==numbers[number].number) {
543  printf("off\n");
544  led_tm1637_off();
545  } else {
546  printf("%u\n", numbers[number].number);
547  led_tm1637_number(numbers[number].number);
548  }
549  }
550 
551  // display date and time on MAX7219 displays
552  led_max7219_number(20000000+rtc_ds1307_time[6]*10000+rtc_ds1307_time[5]*100+rtc_ds1307_time[4], 0x14, 0); // display date on 1st display
553  led_max7219_number(rtc_ds1307_time[2]*1000000+rtc_ds1307_time[1]*10000+rtc_ds1307_time[0]*100, 0x54, 1); // display time on 2nd display
554  led_max7219_on(0xff); // switch displays on
555 
556  // setup timer to count frames
557  printf("setup frame timer: ");
558  rcc_periph_clock_enable(RCC_TIM(FRAME_TIMER)); // enable clock for timer domain
559  timer_reset(TIM(FRAME_TIMER)); // reset timer state
560  timer_set_mode(TIM(FRAME_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
561  timer_set_prescaler(TIM(FRAME_TIMER), (rcc_ahb_frequency/0xffff+1)-1); // set the prescaler to so count up to one second
562  timer_set_period(TIM(FRAME_TIMER), 0xffff/FRAME_RATE); // overflow at the end of every rate
563  timer_update_on_overflow(TIM(FRAME_TIMER)); // only use counter overflow as UEV source (use overflow as timeout)
564  timer_clear_flag(TIM(FRAME_TIMER), TIM_SR_UIF); // clear flag
565  timer_enable_irq(TIM(FRAME_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
566 #if FRAME_TIMER==1
567  nvic_enable_irq(NVIC_TIM1_UP_IRQ); // catch interrupt in service routine
568 #else
569  nvic_enable_irq(NVIC_TIM_IRQ(FRAME_TIMER)); // catch interrupt in service routine
570 #endif
571  timer_set_counter(TIM(FRAME_TIMER),0); // restart timer
572  timer_enable_counter(TIM(FRAME_TIMER)); // enable timer to start counting frames
573  printf("OK\n");
574 
575  // setup PWM for piezo-buzzer
576  printf("setup piezo-buzzer PWM timer: ");
577  rcc_periph_clock_enable(RCC_GPIO(BUZZER_1_PORT)); // enable clock for GPIO port domain
578  rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function (PWM)
579  gpio_primary_remap(AFIO_MAPR_SWJ_MASK, AFIO_MAPR_TIM1_REMAP_PARTIAL_REMAP); // remap TIM1_CH1N to PA7 instead of PB13
580  gpio_set_mode(GPIO(BUZZER_1_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO(BUZZER_1_PIN)); // set pin as output to have a PWM to driver piezo buzzer
581  gpio_clear(GPIO(BUZZER_1_PORT), GPIO(BUZZER_1_PIN)); // ensure it's not beeping
582  rcc_periph_clock_enable(RCC_GPIO(BUZZER_2_PORT)); // enable clock for GPIO peripheral
583  gpio_set_mode(GPIO(BUZZER_2_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO(BUZZER_2_PIN)); // set pin as output to have a PWM to driver piezo buzzer
584  gpio_clear(GPIO(BUZZER_2_PORT), GPIO(BUZZER_2_PIN)); // ensure it's not beeping
585  rcc_periph_clock_enable(RCC_TIM(BUZZER_TIMER)); // enable clock for timer peripheral
586  timer_reset(TIM(BUZZER_TIMER)); // reset timer state
587  timer_set_mode(TIM(BUZZER_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
588  timer_set_prescaler(TIM(BUZZER_TIMER), 0); // no prescaler to keep most precise timer (72MHz/2^16=1099<4kHz)
589  timer_set_period(TIM(BUZZER_TIMER), rcc_ahb_frequency/4000-1); // set the PWM frequency to 4kHz for piezo-buzzer
590  timer_set_oc_value(TIM(BUZZER_TIMER), TIM_OC1, rcc_ahb_frequency/4000/2-1); // duty cycle to 50% (also applies to TIM_OC1N)
591  // no preload is used, although the reference manual says to enable it
592  timer_set_oc_mode(TIM(BUZZER_TIMER), TIM_OC1, TIM_OCM_PWM1); // set timer to generate PWM (also applies to TIM_OC1N)
593  timer_enable_oc_output(TIM(BUZZER_TIMER), TIM_OC1); // enable output to generate PWM
594  timer_enable_oc_output(TIM(BUZZER_TIMER), TIM_OC1N); // enable output to generate PWM (complementary to be louder)
595  timer_enable_break_main_output(TIM(BUZZER_TIMER)); // enable master output
596  timer_generate_event(TIM(BUZZER_TIMER), TIM_EGR_UG); // generate update event to reload registers and reset counter
597  printf("OK\n");
598 
599  // setup GPIO for reading buttons
600  printf("setup display buttons: ");
601  rcc_periph_clock_enable(RCC_GPIO(BUTTONS_DRIVE_PORT)); // enable clock for GPIO port domain
602  // no need to configure the driving line modes since this will be done when they need to be driven individually
603  rcc_periph_clock_enable(RCC_GPIO(BUTTONS_READ_PORT)); // enable clock for GPIO port domain
604  gpio_set_mode(GPIO(BUTTONS_READ_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_READ_PIN0)|GPIO(BUTTONS_READ_PIN1)|GPIO(BUTTONS_READ_PIN2)|GPIO(BUTTONS_READ_PIN3)); // set read lines as input (external pull down resistors are required)
605  gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); // enable PA15 (JTDI per default)
606  uint16_t buttons = 0; // to save button state
607  printf("OK\n");
608 
609  printf("setup clap button: ");
610  rcc_periph_clock_enable(RCC_GPIO(CLAP_BUTTON_PORT)); // enable clock for GPIO port domain
611  gpio_set_mode(GPIO(CLAP_BUTTON_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO(CLAP_BUTTON_PIN)); // set button pin to input
612  gpio_set(GPIO(CLAP_BUTTON_PORT), GPIO(CLAP_BUTTON_PIN)); // pull up
613  rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
614  exti_select_source(EXTI(CLAP_BUTTON_PIN), GPIO(CLAP_BUTTON_PORT)); // mask external interrupt of this pin only for this port
615  exti_set_trigger(EXTI(CLAP_BUTTON_PIN), EXTI_TRIGGER_FALLING); // trigger on falling edge (we are pulling up on one side, and ground is on the other side)
616  exti_enable_request(EXTI(CLAP_BUTTON_PIN)); // enable external interrupt
617  nvic_enable_irq(NVIC_EXTI_IRQ(CLAP_BUTTON_PIN)); // enable interrupt
618  printf("OK\n");
619 
620  // main loop
621  printf("command input: ready\n");
622  bool action = false; // if an action has been performed don't go to sleep
623  char c = '\0'; // to store received character
624  bool char_flag = false; // a new character has been received
625  while (true) { // main loop
626  iwdg_reset(); // kick the dog
627  while (usart_received) { // data received over UART
628  action = true; // action has been performed
629  led_toggle(); // toggle LED to show activity
630  c = usart_getchar(); // store receive character
631  char_flag = true; // notify character has been received
632  }
633  while (cdcacm_received) { // data received over USB
634  action = true; // action has been performed
635  led_toggle(); // toggle LED to show activity
636  c = cdcacm_getchar(); // store receive character
637  char_flag = true; // notify character has been received
638  }
639  while (char_flag) { // user data received
640  char_flag = false; // reset flag
641  action = true; // action has been performed
642  putc(c); // echo receive character
643  if (c=='\r' || c=='\n') { // end of command received
644  if (command_i>0) { // there is a command to process
645  command[command_i] = 0; // end string
646  command_i = 0; // prepare for next command
647  process_command(command); // process user command
648  }
649  } else { // save user command input
650  command[command_i] = c; // save command input
651  if (command_i<LENGTH(command)-2) { // verify if there is place to save next character
652  command_i++; // save next character
653  }
654  }
655  }
656  while (frame_flag) { // a frame has passed
657  frame_flag = false; // reset flag
658  action = true; // action has been performed
659 
660  // display time and frame number
661  char time[] = "00000000"; // time to display
662  time[0] += (rtc_ds1307_time[2]/10)%10; // display hours
663  time[1] += (rtc_ds1307_time[2])%10; // display hours
664  time[1] |= 0x80; // dot for minutes on display
665  time[2] += (rtc_ds1307_time[1]/10)%10; // display minutes
666  time[3] += (rtc_ds1307_time[1])%10; // display minutes
667  if (0==(rtc_seconds%2)) { // even seconds
668  time[3] |= 0x80; // add dot for seconds
669  }
670  time[4] += (rtc_seconds/10)%10; // display seconds
671  time[5] += (rtc_seconds)%10; // display seconds
672  if (0==(frame_count%2)) { // even frames
673  time[5] |= 0x80; // add dot for frame
674  }
675  time[6] += (frame_count/10)%10; // display frame
676  time[7] += (frame_count)%10; // display frame
677  led_max7219_text(time, 1); // display frame time on 2nd display
678 
679  // Morse the scene and take numbers over the buzzer
680  if (morse_delay>0) { // not ready to Morse
681  morse_delay--; // still wait before Morsing
682  } else if (0==morse_delay) { // ready to Morse
683  while (morse_i<LENGTH(morse)) { // until every code has been Morsed
684  if (morse[morse_i]) { // skip empty codes
685  if (morse_i%2) { // switch buzzer on for odd code
686  timer_disable_counter(TIM(BUZZER_TIMER)); // stop buzzing
687  } else {
688  timer_enable_counter(TIM(BUZZER_TIMER)); // start buzzing
689  }
690  morse[morse_i]--; // decrement remaining buzzing time
691  break; // buzz for one frame
692  } else {
693  morse_i++; // got to next code
694  }
695  }
696  if (morse_i>=LENGTH(morse)) { // all codes done
697  morse_delay = -2; // Morse completed
698  gpio_clear(GPIO(BUZZER_1_PORT), GPIO(BUZZER_1_PIN)); // ensure it's not beeping
699  gpio_clear(GPIO(BUZZER_2_PORT), GPIO(BUZZER_2_PIN)); // ensure it's not beeping
700  }
701  }
702 
703  // read button inputs (drive and read each row since they are multiplexed)
704  uint16_t buttons_new = 0; // to save new button states
705  // drive row 0
706  gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUTTONS_DRIVE_PIN0));
708  gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_DRIVE_PIN1)|GPIO(BUTTONS_DRIVE_PIN2)|GPIO(BUTTONS_DRIVE_PIN3));
709  // read all columns for this row
710  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN0)) ? 1 : 0)<<0);
711  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN1)) ? 1 : 0)<<1);
712  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN2)) ? 1 : 0)<<2);
713  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN3)) ? 1 : 0)<<3);
714  // drive row 1
715  gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUTTONS_DRIVE_PIN1));
717  gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_DRIVE_PIN0)|GPIO(BUTTONS_DRIVE_PIN2)|GPIO(BUTTONS_DRIVE_PIN3));
718  // read all columns for this row
719  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN0)) ? 1 : 0)<<4);
720  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN1)) ? 1 : 0)<<5);
721  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN2)) ? 1 : 0)<<6);
722  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN3)) ? 1 : 0)<<7);
723  // drive row 2
724  gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUTTONS_DRIVE_PIN2));
726  gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_DRIVE_PIN0)|GPIO(BUTTONS_DRIVE_PIN1)|GPIO(BUTTONS_DRIVE_PIN3));
727  // read all columns for this row
728  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN0)) ? 1 : 0)<<8);
729  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN1)) ? 1 : 0)<<9);
730  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN2)) ? 1 : 0)<<10);
731  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN3)) ? 1 : 0)<<11);
732  // drive row 3
733  gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUTTONS_DRIVE_PIN3));
735  gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_DRIVE_PIN0)|GPIO(BUTTONS_DRIVE_PIN1)|GPIO(BUTTONS_DRIVE_PIN2));
736  // read all columns for this row
737  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN0)) ? 1 : 0)<<12);
738  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN1)) ? 1 : 0)<<13);
739  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN2)) ? 1 : 0)<<14);
740  buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN3)) ? 1 : 0)<<15);
741  uint16_t buttons_diff = buttons^buttons_new; // get difference
742  buttons = buttons_new; // save new state
743  if (buttons_diff) { // only do something if there is a change
744  //printf("button pressed: %016b\n", buttons);
745  for (uint8_t number=0; number<LENGTH(numbers); number++) { // check buttons for every number
746  if (buttons_diff&(1<<numbers[number].up) || buttons_diff&(1<<numbers[number].down)) { // a button for the number has been pressed
747  if (buttons&(1<<numbers[number].up) && buttons&(1<<numbers[number].down)) { // both buttons are pressed
748  numbers[number].number = 0xffff; // disable number
749  } else if (buttons&(1<<numbers[number].up) && buttons_diff&(1<<numbers[number].up)) { // only up button has been pressed
750  // increment number
751  if (0xffff==numbers[number].number) {
752  numbers[number].number = 0;
753  } else {
754  numbers[number].number = (numbers[number].number+1)%10000;
755  }
756  } else if (buttons&(1<<numbers[number].down) && buttons_diff&(1<<numbers[number].down)) { // only down button has been pressed
757  // decrement number
758  if (0xffff==numbers[number].number) {
759  numbers[number].number = 9999;
760  } else if (0==numbers[number].number) {
761  numbers[number].number = 0xffff;
762  } else {
763  numbers[number].number -= 1;
764  }
765  }
766  // display numbers on TM1637
767  mux_select(numbers[number].display);
768  if (0xffff==numbers[number].number) {
769  led_tm1637_off();
770  } else {
771  led_tm1637_number(numbers[number].number);
772  }
773  // save number
774  uint8_t bytes[2] = {numbers[number].number>>8, numbers[number].number};
775  if (!rtc_ds1307_write_rom(number*LENGTH(bytes), bytes, LENGTH(bytes))) {
776  printf("could not set number on EEPROM\n");
777  }
778  }
779  }
780  if ((buttons_diff&(1<<BUTTON_SECONDS_UP) && buttons&(1<<BUTTON_SECONDS_UP)) || (buttons_diff&(1<<BUTTON_SECONDS_DOWN) && buttons&(1<<BUTTON_SECONDS_DOWN))) { // buttons for time pressed
781  rtc_ds1307_time = rtc_ds1307_read_time(); // get time/date from external RTC
782  if (rtc_ds1307_time==NULL) {
783  printf("could not get time from DS1307: resetting\n");
784  rtc_ds1307_setup(); // resetting peripheral
785  } else {
786  // adjust time
787  if (buttons_diff&(1<<BUTTON_SECONDS_UP) && buttons&(1<<BUTTON_SECONDS_UP)) { // up pressed
788  // increment time by one second
789  if (!(23==rtc_ds1307_time[2] && 59==rtc_ds1307_time[1] && 59==rtc_ds1307_time[0])) { // don't handle date changes since it's too complex
790  rtc_ds1307_time[0] += 1;
791  while (rtc_ds1307_time[0]>=60) {
792  rtc_ds1307_time[0] -= 60;
793  rtc_ds1307_time[1] += 1;
794  }
795  while (rtc_ds1307_time[1]>=60) {
796  rtc_ds1307_time[1] -= 60;
797  rtc_ds1307_time[2] += 1;
798  }
799  while (rtc_ds1307_time[2]>=24) {
800  rtc_ds1307_time[2] -= 24;
801  }
802  }
803  } else if (buttons_diff&(1<<BUTTON_SECONDS_DOWN) && buttons&(1<<BUTTON_SECONDS_DOWN)) { // down pressed
804  // decrement time by one second
805  if (!(0==rtc_ds1307_time[2] && 0==rtc_ds1307_time[1] && 0==rtc_ds1307_time[0])) { // don't handle date changes since it's too complex
806  if (rtc_ds1307_time[0]>0) {
807  rtc_ds1307_time[0] -= 1;
808  } else {
809  rtc_ds1307_time[0] = 59;
810  if (rtc_ds1307_time[1]>0) {
811  rtc_ds1307_time[1] -= 1;
812  } else {
813  rtc_ds1307_time[1] = 59;
814  rtc_ds1307_time[2] -= 1; // it can't be 0
815  }
816  }
817  }
818  }
819  rtc_seconds = rtc_ds1307_time[0]; // save seconds since this is only updated every minute
820  if (!rtc_ds1307_write_time(rtc_ds1307_time[0], rtc_ds1307_time[1], rtc_ds1307_time[2], rtc_ds1307_time[3], rtc_ds1307_time[4], rtc_ds1307_time[5], rtc_ds1307_time[6])) { // save adjusted time to DS1307
821  printf("could not set time on DS1307: resetting\n");
822  rtc_ds1307_setup(); // resetting peripheral
823  }
824  }
825  }
826  }
827 
828  }
829  while (rtc_tick_flag) { // the external RTC ticked
830  rtc_tick_flag = false; // reset flag
831  action = true; // action has been performed
832  led_toggle(); // toggle LED (good to indicate if main function is stuck)
833  if (standby_timer>=STANDBY_TIMEOUT) { // standby timeout complete
834  // go into standby mode
835  printf("shutting down\n");
836  // increment and save take and video/audio numbers if there has been a clap
837  if (-2==morse_delay) {
838  morse_delay = -1; // prevent incrementing multiple times
839  uint8_t bytes[LENGTH(numbers)*2] = {0}; // all numbers to write again in EEPROM
840  for (uint8_t number=0; number<LENGTH(numbers); number++) { // increment numbers
841  if (number!=0 && number!=1 && numbers[number].number!=0xffff) { // don't increment episode, scene, or disabled numbers
842  numbers[number].number = (numbers[number].number+1)%10000;
843  printf("incrementing %u to %u\n", number, numbers[number].number);
844  }
845  bytes[2*number+0] = numbers[number].number>>8; // set number to save
846  bytes[2*number+1] = numbers[number].number; // set number to save
847  }
848  if (!rtc_ds1307_write_rom(0, bytes, LENGTH(bytes))) { // save numbers to EEPROM
849  printf("could not set numbers on EEPROM\n");
850  }
851  }
852  for (uint8_t tm1637=0; tm1637<7; tm1637++) { // switch off TM1637 displays
853  mux_select(tm1637); // selecting TM1637 display
854  led_tm1637_off(); // switch off TM1637 display
855  }
856  led_max7219_off(0xff); // switch off MAX7219 displays
857  timer_disable_counter(TIM(BUZZER_TIMER)); // stop buzzing
858  gpio_clear(GPIO(POWER_SWITCH_PORT), GPIO(POWER_SWITCH_PIN)); // switch power of by disconnecting from battery
859  SCB_SCR |= SCB_SCR_SLEEPDEEP; // enable deep sleep
860  pwr_set_standby_mode(); // go to deep sleep
861  while (true); // we should be shut down at this point
862  }
863  if (rtc_seconds>=60) { // one minute passed
864  rtc_ds1307_time = rtc_ds1307_read_time(); // get time/date from external RTC
865  if (rtc_ds1307_time==NULL) {
866  printf("could not get time from DS1307: resetting\n");
867  rtc_ds1307_setup(); // resetting peripheral
868  } else {
869  rtc_seconds = rtc_ds1307_time[0]; // get actual number of seconds
870  if (0==rtc_ds1307_time[1] && 0==rtc_ds1307_time[2]) { // new day arrived
871  led_max7219_number(20000000+rtc_ds1307_time[6]*10000+rtc_ds1307_time[5]*100+rtc_ds1307_time[4], 0x14, 0); // display date on 1st display
872  }
873  }
874  }
875  }
876  while (keep_alive_flag) { // power switch is detecting movement to keep clapperboard running
877  action = true; // action has been performed
878  keep_alive_flag = false; // clear flag
879  standby_timer = 0; // restart standby timer
880  }
881  while (clap_flag) { // clap detected
882  action = true; // action has been performed
883  clap_flag = false; // clear flag
884  if (morse_delay<0) { // if beeping is not already queued
885  encode_morse(); // encode scene and take number into Morse code
886  morse_delay = FRAME_RATE; // wait 1 second
887  }
888  }
889  if (action) { // go to sleep if nothing had to be done, else recheck for activity
890  action = false;
891  } else {
892  __WFI(); // go to sleep and wait for interrupt
893  }
894  } // main loop
895 }
896 
899 {
900  exti_reset_request(EXTI(SQUARE_WAVE_PIN)); // reset interrupt
901  frame_count = 0; // re-sync frame counter to second
902  rtc_seconds++; // increment number of seconds passed
903  if (standby_timer<STANDBY_TIMEOUT) { // timeout countdown not complete
904  standby_timer++; // continue counting down
905  }
906  rtc_tick_flag = true; // let main know a second passed
907 }
908 
910 #if FRAME_TIMER==1
911 void tim1_up_isr(void)
912 #else
913 void TIM_ISR(FRAME_TIMER)(void)
914 #endif
915 {
916  if (timer_get_flag(TIM(FRAME_TIMER), TIM_SR_UIF)) { // overflow update event happened
917  timer_clear_flag(TIM(FRAME_TIMER), TIM_SR_UIF); // clear flag
918  frame_count++; // increment frame counter since frame finished
919  frame_flag = true; // let main know the counter changed
920  } else { // no other interrupt should occur
921  while (true); // unhandled exception: wait for the watchdog to bite
922  }
923 }
924 
927 {
928  exti_reset_request(EXTI(POWER_BUTTON_PIN)); // reset interrupt
929  keep_alive_flag = true; // perform button action
930 }
931 
934 {
935  exti_reset_request(EXTI(CLAP_BUTTON_PIN)); // reset interrupt
936  clap_flag = true; // perform clap action
937 }
number to be display and changed using the buttons
Definition: main.c:91
void board_setup(void)
setup board peripherals
Definition: global.c:82
#define FRAME_TIMER
timer to count frame time
Definition: main.c:71
uint8_t morse[2 *4 *5 *2]
to encode 2 4-digit numbers (scene and take) into Morse code (5 sign+space)
Definition: main.c:127
bool led_tm1637_number(uint16_t number)
display number
Definition: led_tm1637.c:294
bool rtc_ds1307_write_date(uint8_t date)
write date into RTC IC
Definition: rtc_ds1307.c:319
#define TIM_ISR(x)
get interrupt service routine for timer base on TIM identifier
Definition: global.h:49
size_t putc(char c)
print a single character on user output
Definition: main.c:136
volatile uint8_t frame_count
number of frames passed
Definition: main.c:73
void rtc_ds1307_setup(void)
setup communication with RTC IC configure the I2C port defined in the sources
Definition: rtc_ds1307.c:40
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:355
volatile bool usart_received
how many bytes available in the received buffer since last read
Definition: usart.c:53
#define MUX_S2_PIN
pin to select multiplexer output
Definition: main.c:88
void cdcacm_putchar(char c)
send character over USB (non-blocking)
Definition: usb_cdcacm.c:391
bool rtc_ds1307_write_rom(uint16_t start, uint8_t *data, uint8_t length)
write to ROM on AT24Cxx EEPROM
Definition: rtc_ds1307.c:415
struct number_t numbers[]
episode, scene, take, video 1, audio 1, video 2, audio 2 number (in this order)
Definition: main.c:98
static char command[32]
user input command
Definition: main.c:132
bool rtc_ds1307_write_year(uint8_t year)
write year into RTC IC
Definition: rtc_ds1307.c:343
#define RCC_GPIO(x)
get RCC for GPIO based on GPIO identifier
Definition: global.h:41
#define FRAME_RATE
frame rate in frames per second
Definition: main.c:72
volatile bool keep_alive_flag
flag to restart shutdown counter on power switch activity
Definition: main.c:55
void led_max7219_setup(uint8_t displays)
setup communication with MAX7219 IC
Definition: led_max7219.c:186
bool rtc_ds1307_oscillator_enable(void)
enable RTC IC oscillator
Definition: rtc_ds1307.c:230
bool rtc_ds1307_write_month(uint8_t month)
write month into RTC IC
Definition: rtc_ds1307.c:331
#define POWER_BUTTON_PORT
port to detect power switching activity by shaking (to keep alive)
Definition: main.c:61
void led_off(void)
switch off board LED
Definition: global.c:68
bool rtc_ds1307_write_minutes(uint8_t minutes)
write minutes into RTC IC
Definition: rtc_ds1307.c:283
#define BUTTONS_READ_PIN2
pin used to read buttons column 2
Definition: main.c:119
#define NVIC_TIM_IRQ(x)
get NVIC IRQ for timer base on TIM identifier
Definition: global.h:47
#define BUTTONS_DRIVE_PIN0
pin used to drive buttons row 0
Definition: main.c:112
uint8_t up
which of the 16 buttons is to increment the number
Definition: main.c:93
bool led_max7219_text(char *text, uint8_t display)
display text
Definition: led_max7219.c:240
library to communicate with a Titan Micro TM1637 IC attached to a 7-segment displays (API) ...
uint16_t number
number to display (0-9999, 0xffff=off)
Definition: main.c:92
#define WATCHDOG_PERIOD
watchdog period in ms
Definition: main.c:48
static void process_command(char *str)
process user command
Definition: main.c:165
#define BUTTONS_DRIVE_PIN3
pin used to drive buttons row 3
Definition: main.c:115
#define NVIC_EXTI_IRQ(x)
get NVIC IRQ for external interrupt base on external interrupt/pin
Definition: global.h:92
#define MUX_S1_PORT
port to select multiplexer output
Definition: main.c:85
#define CLAP_BUTTON_PIN
port for button to detect clap action
Definition: main.c:123
#define MUX_EN_PIN
pin to enable multiplexer
Definition: main.c:82
#define BUTTONS_READ_PIN0
pin used to read buttons column 0
Definition: main.c:117
global definitions and methods (API)
#define STANDBY_TIMEOUT
number of seconds after last shake before going down
Definition: main.c:68
#define GPIO(x)
get GPIO based on GPIO identifier
Definition: global.h:39
#define POWER_BUTTON_PIN
pin to detect power switching activity by shaking (to keep alive)
Definition: main.c:62
volatile uint8_t rtc_seconds
number of seconds passed, incremented by the square wave
Definition: main.c:66
#define MORSE_DOT
Morse code variables, to buzz/beep scene and take.
Definition: main.c:126
#define BUTTONS_DRIVE_PIN1
pin used to drive buttons row 1
Definition: main.c:113
#define SQUARE_WAVE_PIN
pin connected to RTC DS1307 square wave output
Definition: main.c:65
void cdcacm_setup(void)
setup USB CDC ACM peripheral
Definition: usb_cdcacm.c:357
#define BUZZER_TIMER
timer to generate scene and take count
Definition: main.c:75
#define BUZZER_2_PIN
use timer channel 1 (and it&#39;s negative) to driver buzzer
Definition: main.c:79
#define RCC_TIM(x)
get RCC for timer based on TIM identifier
Definition: global.h:45
uint8_t rtc_ds1307_read_month(void)
read month from RTC IC
Definition: rtc_ds1307.c:141
#define MUX_EN_PORT
port to enable multiplexer
Definition: main.c:81
void led_max7219_test(bool test, uint8_t display)
switch display in test or normal operation mode
Definition: led_max7219.c:219
int16_t morse_delay
number of frames before beeping (-1=no need to beep yet, -2=beeping completed)
Definition: main.c:129
#define POWER_SWITCH_PORT
port to switch power of all devices (including this micro-controller)
Definition: main.c:59
#define BUTTONS_READ_PIN3
pin used to read buttons column 3
Definition: main.c:120
volatile bool frame_flag
flag set when a frame has passed
Definition: main.c:54
#define BUZZER_1_PIN
use timer channel 1 (and it&#39;s negative) to driver buzzer
Definition: main.c:77
uint8_t rtc_ds1307_read_year(void)
read year from RTC IC
Definition: rtc_ds1307.c:153
uint8_t * rtc_ds1307_read_time(void)
read time from RTC IC
Definition: rtc_ds1307.c:164
uint8_t display
which display on the channel multiplexer
Definition: main.c:95
volatile bool clap_flag
flag set on clap
Definition: main.c:56
#define BUTTONS_DRIVE_PORT
port used to drive the buttons rows
Definition: main.c:111
uint8_t rtc_ds1307_read_seconds(void)
read seconds from RTC IC
Definition: rtc_ds1307.c:73
#define MUX_S0_PIN
pin to select multiplexer output
Definition: main.c:84
volatile uint8_t cdcacm_received
how many bytes available in the received buffer since last read
Definition: usb_cdcacm.c:210
#define CLAP_BUTTON_PORT
port for button to detect clap action
Definition: main.c:122
uint8_t command_i
user input command index
Definition: main.c:134
#define EXTI(x)
get external interrupt based on pin identifier
Definition: global.h:90
#define MUX_S0_PORT
port to select multiplexer output
Definition: main.c:83
void led_max7219_on(uint8_t display)
switch display on
Definition: led_max7219.c:209
void led_max7219_number(uint32_t number, uint8_t dots, uint8_t display)
display number
Definition: led_max7219.c:254
bool rtc_ds1307_write_hours(uint8_t hours)
write hours into RTC IC
Definition: rtc_ds1307.c:295
uint8_t rtc_ds1307_read_date(void)
read date from RTC IC
Definition: rtc_ds1307.c:129
#define SQUARE_WAVE_PORT
port connected to RTC DS1307 square wave output
Definition: main.c:64
bool rtc_ds1307_write_seconds(uint8_t seconds)
write seconds into RTC IC
Definition: rtc_ds1307.c:267
void usart_putchar_nonblocking(char c)
send character over USART (non-blocking)
Definition: usart.c:113
bool rtc_ds1307_read_rom(uint16_t start, uint8_t *data, uint16_t length)
read ROM from AT24Cxx EEPROM
Definition: rtc_ds1307.c:205
#define BUTTON_SECONDS_DOWN
which of the 16 buttons is to decrement the seconds
Definition: main.c:108
volatile bool rtc_tick_flag
flag set when RTC ticked
Definition: main.c:53
#define POWER_SWITCH_PIN
pin to switch power of all devices (including this micro-controller)
Definition: main.c:60
static void mux_select(uint8_t output)
select clock output for TM1637 display using the analog multiplexer
Definition: main.c:243
#define MUX_S2_PORT
port to select multiplexer output
Definition: main.c:87
#define EXTI_ISR(x)
get interrupt service routine for timer base on external interrupt/pin
Definition: global.h:105
library for USART communication (API)
void led_tm1637_setup(void)
setup communication with TM1637 IC
Definition: led_tm1637.c:165
#define BUTTONS_READ_PORT
port used to read the buttons columns
Definition: main.c:116
#define BUTTON_SECONDS_UP
which of the 16 buttons is to increment the seconds
Definition: main.c:107
void led_max7219_off(uint8_t display)
switch display off
Definition: led_max7219.c:214
#define BUTTONS_READ_PIN1
pin used to read buttons column 1
Definition: main.c:118
#define BUTTONS_DRIVE_PIN2
pin used to drive buttons row 2
Definition: main.c:114
void led_max7219_intensity(uint8_t intensity, uint8_t digits, uint8_t display)
set display intensity
Definition: led_max7219.c:228
bool led_tm1637_off(void)
switch display on
Definition: led_tm1637.c:276
char usart_getchar(void)
get character received over USART (blocking)
Definition: usart.c:99
library for USB CDC ACM communication (API)
library to communicate with a Maxim MAX7219 IC attached to a 8-digit 7-segment (API) ...
#define LENGTH(x)
get the length of an array
Definition: global.h:26
static void encode_morse(void)
encode scene and take into Morse code (in morse variable)
Definition: main.c:296
bool led_tm1637_time(uint8_t hours, uint8_t minutes)
display time
Definition: led_tm1637.c:300
#define TIM(x)
get TIM based on TIM identifier
Definition: global.h:43
uint8_t morse_i
index in Morse array
Definition: main.c:128
void led_toggle(void)
toggle board LED
Definition: global.c:77
uint8_t down
which of the 16 buttons is to decrement the number
Definition: main.c:94
bool rtc_ds1307_oscillator_disabled(void)
verify if oscillator is disabled
Definition: rtc_ds1307.c:46
library to communicate with the Maxim DS1307 I2C RTC IC (API)
volatile uint16_t standby_timer
number of seconds since last power-up activity (using shake sensor)
Definition: main.c:69
bool rtc_ds1307_write_square_wave(uint16_t frequency)
write square wave output frequency (in Hz)
Definition: rtc_ds1307.c:241
void main(void)
program entry point this is the firmware function started by the micro-controller ...
Definition: main.c:419
#define MUX_S1_PIN
pin to select multiplexer output
Definition: main.c:86
void usart_setup(void)
setup USART peripheral
Definition: usart.c:55
uint8_t rtc_ds1307_read_hours(void)
read hours from RTC IC
Definition: rtc_ds1307.c:97
void led_on(void)
switch on board LED
Definition: global.c:59
char cdcacm_getchar(void)
get character received over USB (blocking)
Definition: usb_cdcacm.c:379
#define BUZZER_1_PORT
use timer channel 1 (and it&#39;s negative) to driver buzzer
Definition: main.c:76
#define BUZZER_2_PORT
use timer channel 1 (and it&#39;s negative) to driver buzzer
Definition: main.c:78
uint8_t rtc_ds1307_read_minutes(void)
read minutes from RTC IC
Definition: rtc_ds1307.c:85