led_clock-old/main.c

402 lines
15 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-03-23 07:50:53 +01:00
/** @file main.c
2016-03-24 10:37:42 +01:00
@author King Kévin <kingkevin@cuvoodoo.info>
@date 2016
@brief show the time on a LED strip
The LED strip consists of 60 WS2812b LEDs.
The time is read from a DS1307 RTC module.
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
/* 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-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-02-18 11:02:28 +01:00
#include "led_ws2812b.h" // WS2812b LEDs utilities
2016-03-23 07:50:53 +01:00
#include "rtc_ds1307.h" // Real Time Clock DS1307 utilities
2016-01-17 14:54:54 +01:00
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 time_flag = false; /**< flag set when time changed */
2016-03-24 10:37:42 +01:00
/** @} */
2016-02-18 11:34:08 +01:00
2016-03-25 19:19:34 +01:00
#define TICKS_PER_SECOND 256 /**< the number of ticks in one second */
2016-03-25 20:24:46 +01:00
#define SQUARE_WAVE_FREQUENCY 4096 /**< square wave output frequency from the RTC IC */
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
* @{
*/
/** 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-03-24 10:37:42 +01:00
/** RGB values for the WS2812b clock LEDs */
uint8_t clock_leds[WS2812B_LEDS*3] = {0};
/** current time in tick */
volatile uint32_t current_time = 0;
2016-01-17 14:54:54 +01:00
int _write(int file, char *ptr, int len)
{
int i;
if (file == STDOUT_FILENO || file == STDERR_FILENO) {
for (i = 0; i < len; i++) {
if (ptr[i] == '\n') { // add carrier return before line feed. this is recommended for most UART terminals
usart_putchar_nonblocking('\r'); // a second line feed doesn't break the display
2016-01-18 16:23:35 +01:00
cdcacm_putchar('\r'); // a second line feed doesn't break the display
2016-01-17 14:54:54 +01:00
}
usart_putchar_nonblocking(ptr[i]); // send byte over USART
2016-01-18 16:23:35 +01:00
cdcacm_putchar(ptr[i]); // send byte over USB
2016-01-17 14:54:54 +01:00
}
return i;
}
errno = EIO;
return -1;
}
2016-01-29 00:24:49 +01:00
void led_on(void)
2016-01-17 14:54:54 +01:00
{
2016-03-25 19:19:34 +01:00
#if defined(SYSTEM_BOARD) || defined(BLUE_PILL)
2016-01-29 00:24:49 +01:00
gpio_clear(LED_PORT, LED_PIN);
2016-03-25 19:19:34 +01:00
#elif defined(MAPLE_MINI)
2016-01-29 00:24:49 +01:00
gpio_set(LED_PORT, LED_PIN);
#endif
}
void led_off(void)
{
2016-03-25 19:19:34 +01:00
#if defined(SYSTEM_BOARD) || defined(BLUE_PILL)
2016-01-29 00:24:49 +01:00
gpio_set(LED_PORT, LED_PIN);
2016-03-25 19:19:34 +01:00
#elif defined(MAPLE_MINI)
2016-01-29 00:24:49 +01:00
gpio_clear(LED_PORT, LED_PIN);
#endif
2016-01-17 14:54:54 +01:00
}
2016-01-29 00:24:49 +01:00
void led_toggle(void)
2016-01-17 14:54:54 +01:00
{
2016-01-29 00:24:49 +01:00
gpio_toggle(LED_PORT, LED_PIN);
2016-01-17 14:54:54 +01:00
}
2016-03-25 11:42:41 +01:00
/** @brief switch off all clock LEDs
* @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-03-25 11:42:41 +01:00
/** @brief set hours mark on clock LEDs
* @note LEDs need to be set separately
*/
2016-02-19 11:31:30 +01:00
static void clock_hours(void)
2016-02-18 11:34:08 +01:00
{
for (uint8_t hour=0; hour<12; hour++) {
2016-02-19 11:31:30 +01:00
uint16_t led = WS2812B_LEDS/12*hour;
2016-02-18 11:34:08 +01:00
clock_leds[led*3+0] = 0xff;
clock_leds[led*3+1] = 0xff;
clock_leds[led*3+2] = 0xff;
}
}
2016-03-25 11:42:41 +01:00
/** @brief show time on LED clock
* @param[in] time in ticks to show
* @details show hours and minutes progress as full arcs, show second position as marker. the brightness of the LED shows the progress of the unit. hours are blue, minutes green, seconds red
* @note LEDs need to be set separately
*/
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-02-23 21:52:00 +01:00
uint32_t led_hour = (WS2812B_LEDS*(255*(uint64_t)(time%ticks_midday)))/ticks_midday; // scale to LED brightnesses for hours
uint32_t led_minute = (WS2812B_LEDS*(255*(uint64_t)(time%ticks_hour)))/ticks_hour; // scale to LED brightnesses for minutes
if (led_hour>=WS2812B_LEDS*255 || led_minute>=WS2812B_LEDS*255) { // 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)
for (uint16_t led=0; 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-02-23 23:38:08 +01:00
for (uint16_t led=0; 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)
for (uint16_t led=0; 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-02-23 23:38:08 +01:00
for (uint16_t led=0; 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-02-23 23:56:04 +01:00
uint16_t led_second = (WS2812B_LEDS*(time%ticks_minute))/ticks_minute; // get LED for seconds
uint8_t brightness_second = (255*(time%ticks_second))/ticks_second; // get brightness for seconds
// set second LED
clock_leds[led_second*3+0] = brightness_second;
2016-03-26 14:58:21 +01:00
//clock_leds[led_second*3+1] = 0; // clear other colors (minutes/hours indication)
//clock_leds[led_second*3+2] = 0; // clear other colors (minutes/hours indication)
2016-02-23 23:56:04 +01:00
// set previous LED
led_second = (led_second==0) ? WS2812B_LEDS-1 : led_second-1; // previous LED
clock_leds[led_second*3+0] = 0xff-brightness_second;
2016-03-26 14:58:21 +01:00
//clock_leds[led_second*3+1] = 0; // clear other colors (minutes/hours indication)
//clock_leds[led_second*3+2] = 0; // clear other colors (minutes/hours indication)
2016-02-19 11:31:30 +01:00
}
2016-03-25 11:42:41 +01:00
/** @brief set the LEDs
* @details set the LED colors on WS2812b LEDs
* @note WS2812b LED color values need to be transmitted separately
*/
2016-03-26 14:58:21 +01:00
static void clock_leds_set(void)
2016-02-18 11:34:08 +01:00
{
2016-03-26 14:58:21 +01:00
static uint8_t clock_leds_old[WS2812B_LEDS*3] = {0}; // backup so to only set when value changed
2016-02-18 11:34:08 +01:00
for (uint16_t i=0; i<LENGTH(clock_leds)/3; i++) {
2016-03-26 14:58:21 +01:00
if (clock_leds[i*3+0]!=clock_leds_old[i*3+0] || clock_leds[i*3+1]!=clock_leds_old[i*3+1] || clock_leds[i*3+2]!=clock_leds_old[i*3+2]) { // only set when value changed (since this costs time)
clock_leds_old[i*3+0] = clock_leds[i*3+0]; // memorise change
clock_leds_old[i*3+1] = clock_leds[i*3+1]; // memorise change
clock_leds_old[i*3+2] = clock_leds[i*3+2]; // memorise change
ws2812b_set_rgb(i,clock_leds[i*3+0],clock_leds[i*3+1],clock_leds[i*3+2]); // set new value (this costs time)
}
2016-02-18 11:34:08 +01:00
}
}
2016-03-25 13:57:56 +01:00
/** @brief set the time on the LEDs
* @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-03-25 20:24:46 +01:00
ws2812b_transmit(); // transmit set color
}
/** @brief incrementally set the time on the LEDs
* @details this will have an animation where time is incremented until it reaches the provided time
* @param[in] time time to set
*/
static void clock_animate_time(uint32_t time)
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-03-24 10:37:42 +01:00
/** @brief program entry point
* this is the firmware function started by the micro-controller
*/
2016-01-17 14:54:54 +01:00
int main(void)
{
2016-01-17 14:54:54 +01:00
SCB_VTOR = (uint32_t) 0x08002000; // relocate vector table because of the bootloader
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-01-29 11:25:30 +01:00
usart_setup(); // setup USART (for printing)
cdcacm_setup(); // setup USB CDC ACM (for printing)
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
// setup LED
2016-01-29 11:25:30 +01:00
rcc_periph_clock_enable(LED_RCC); // enable clock for LED
2016-01-29 00:24:49 +01:00
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 to indicate setup started
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
gpio_set_mode(BUTTON_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, BUTTON_PIN); // set button pin to input
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
exti_set_trigger(BUTTON_EXTI, EXTI_TRIGGER_BOTH); // trigger on both edge
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-03-23 07:50:53 +01:00
// setup WS2812b LEDs
2016-02-18 11:02:28 +01:00
ws2812b_setup(); // setup WS2812b LEDs
2016-02-23 20:28:55 +01:00
clock_hours(); // show hour markers
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-02-23 20:28:55 +01:00
ws2812b_transmit(); // transmit set color
2016-02-18 11:02:28 +01:00
2016-03-23 07:50:53 +01:00
// setup RTC module
rtc_setup(); // setup RTC module
2016-03-25 19:19:34 +01:00
rtc_write_square_wave(SQUARE_WAVE_FREQUENCY); // set square wave output frequency
2016-03-23 07:50:53 +01:00
2016-02-18 11:02:28 +01:00
printf("welcome to the CuVoodoo LED clock\n"); // print welcome message
2016-01-29 00:24:49 +01:00
led_on(); // switch on LED to indicate setup completed
2016-01-18 16:23:35 +01:00
2016-03-25 13:57:56 +01:00
// verify is RTC is running
if (rtc_oscillator_disabled()) {
2016-03-25 13:57:56 +01:00
printf("/!\\ RTC oscillator is disabled\n");
}
2016-03-25 13:57:56 +01:00
2016-03-25 20:24:46 +01:00
// get date and time
2016-03-25 13:57:56 +01:00
uint16_t* rtc_time = rtc_read_time(); // get RTC time/date
printf("current date: %04d-%02d-%02d %02d:%02d:%02d\n", rtc_time[6], rtc_time[5], rtc_time[4], rtc_time[2], rtc_time[1], rtc_time[0]);
current_time = rtc_time[2]*ticks_hour+rtc_time[1]*ticks_minute+rtc_time[0]*ticks_second; // the current time
2016-03-25 20:24:46 +01:00
clock_animate_time(current_time); // set time with animation
2016-03-25 13:57:56 +01:00
2016-03-23 09:52:53 +01:00
//rtc_write_time(0,52,9,4,23,3,2016);
//rtc_oscillator_enable();
2016-02-23 23:56:04 +01:00
2016-02-24 10:33:21 +01:00
printf("input commands\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-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 (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-01-18 16:23:35 +01:00
}
2016-03-25 19:19:34 +01:00
while (button_flag) { // user pressed button
button_flag = false; // reset flag
action = true; // action has been performed
led_toggle(); // toggle LED
}
while (time_flag) { // time passed
time_flag = false; // reset flag
2016-03-25 18:00:44 +01:00
action = true; // action has been performed
led_toggle();
if ((current_time%ticks_minute)==0) { // sync each minute
rtc_time = rtc_read_time(); // get RTC time/date
current_time = rtc_time[2]*ticks_hour+rtc_time[1]*ticks_minute+rtc_time[0]*ticks_second; // calculate current time
printf("it is now %02lu:%02lu:%02lu\n", current_time/ticks_hour, (current_time%ticks_hour)/ticks_minute, (current_time%ticks_minute)/ticks_second); // display time
}
clock_set_time(current_time); // set time
if ((current_time%ticks_second)==0) { // print each second
//printf("%02lu:%02lu:%02lu\n", current_time/ticks_hour, (current_time%ticks_hour)/ticks_minute, (current_time%ticks_minute)/ticks_second); // display time
2016-03-25 20:24:46 +01:00
}
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)
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
#if defined(SQUARE_WAVE_EXTI) && defined(SQUARE_WAVE_IRQ) && defined(SQUARE_WAVE_ISR) && defined(SQUARE_WAVE_HANDLING) && !SQUARE_WAVE_HANDLING
/** @brief square wave input interrupt */
void SQUARE_WAVE_ISR(void)
{
static uint16_t square_wave_prescale = 0; /**< prescale the square wave output to generate a tick */
exti_reset_request(SQUARE_WAVE_EXTI); // reset interrupt
square_wave_prescale = (square_wave_prescale+1)%(SQUARE_WAVE_FREQUENCY/TICKS_PER_SECOND); // increment pre-scale counter
if (square_wave_prescale==0) { // pre-scale completed
current_time++; // increment time
time_flag = true; // update flag
}
}
#endif