application: first test for pinkeluhr firmware

This commit is contained in:
King Kévin 2020-08-20 13:54:42 +02:00
parent 69402a5525
commit f58d7c6da4
1 changed files with 319 additions and 13 deletions

View File

@ -24,6 +24,7 @@
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
#include <libopencm3/stm32/desig.h> // design utilities
#include <libopencm3/stm32/flash.h> // flash utilities
#include <libopencm3/stm32/usart.h> // USART utilities for MP3 player
/* own libraries */
#include "global.h" // board definitions
@ -34,6 +35,8 @@
#include "usb_cdcacm.h" // USB CDC ACM utilities
#include "terminal.h" // handle the terminal interface
#include "menu.h" // menu utilities
#include "led_tm1637.h" // TM1637 7-segment display controller
#include "led_sk6812rgbw.h" // SK6812 RGBW LED controller
/** watchdog period in ms */
#define WATCHDOG_PERIOD 10000
@ -41,16 +44,12 @@
/** set to 0 if the RTC is reset when the board is powered on, only indicates the uptime
* set to 1 if VBAT can keep the RTC running when the board is unpowered, indicating the date and time
*/
#if defined(CORE_BOARD)
#define RTC_DATE_TIME 1
#else
#define RTC_DATE_TIME 0
#endif
/** number of RTC ticks per second
* @note use integer divider of oscillator to keep second precision
*/
#define RTC_TICKS_SECOND 4
#define RTC_TICKS_SECOND 16
#if defined(RTC_DATE_TIME) && RTC_DATE_TIME
/** the start time from which to RTC ticks count
@ -62,12 +61,55 @@ const time_t rtc_offset = 1577833200; // We 1. Jan 00:00:00 CET 2020
/** RTC time when device is started */
static time_t time_start = 0;
/** time when the door has been closed/locked (RTC time) */
static uint32_t timer_door_closed = 0;
/** @defgroup main_flags flag set in interrupts to be processed in main task
* @{
*/
static volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
static volatile bool mp3_rx_flag = false; /**< if data has been received from the MP3 player */
static volatile bool door_flag = false; /**< set when the door lock switch changes */
/** @} */
/** switch in the door to verify if its locked (closed when locked) */
#define DOOR_PIN PB8
/** USART port used to communicate with catalex MP3 player */
#define MP3_UART 2
/** data received from MP3 player */
static volatile uint8_t mp3_rx_data[10];
/** number of byte received from MP3 player */
static volatile uint8_t mp3_rx_len;
/** catalex MP3 player commands */
enum mp3_commands_t {
MP3_CMD_NEXT_SONG = 0x01,
MP3_CMD_PREV_SONG = 0x02,
MP3_CMD_PLAY_W_INDEX = 0x03,
MP3_CMD_VOLUME_UP = 0x04,
MP3_CMD_VOLUME_DOWN = 0x05,
MP3_CMD_SET_VOLUME = 0x06,
MP3_CMD_SINGLE_CYCLE_PLAY = 0x08,
MP3_CMD_SEL_DEV = 0x09,
MP3_CMD_SLEEP_MODE = 0x0A,
MP3_CMD_WAKE_UP = 0x0B,
MP3_CMD_RESET = 0x0C,
MP3_CMD_PLAY = 0x0D,
MP3_CMD_PAUSE = 0x0E,
MP3_CMD_PLAY_FOLDER_FILE = 0x0F,
MP3_CMD_STOP_PLAY = 0x16,
MP3_CMD_FOLDER_CYCLE = 0x17,
MP3_CMD_SHUFFLE_PLAY = 0x18,
MP3_CMD_SET_SINGLE_CYCLE = 0x19,
MP3_CMD_SET_DAC = 0x1A,
MP3_CMD_PLAY_W_VOL = 0x22,
MP3_CMD_QUERY_STATUS = 0x42,
MP3_CMD_QUERY_FLDR_TRACKS = 0x4e,
MP3_CMD_QUERY_TOT_TRACKS = 0x48,
MP3_CMD_QUERY_FLDR_COUNT = 0x4f,
};
size_t putc(char c)
{
size_t length = 0; // number of characters printed
@ -122,6 +164,139 @@ static void command_reset(void* argument);
*/
static void command_bootloader(void* argument);
/** process response received from MP3 player
* @return if a valid response has been received
*/
static bool mp3_response(void)
{
if (mp3_rx_len < 10) { // responses are always at least 10 bytes long
return false;
}
if (0x7e != mp3_rx_data[0]) { // response always starts with 0x7e
mp3_rx_len = 0; // reset message
return false;
}
if (0xff != mp3_rx_data[1]) { // version is always 0xff
mp3_rx_len = 0; // reset message
return false;
}
if (mp3_rx_data[2] > LENGTH(mp3_rx_data) - 3) { // message is longer than buffer
mp3_rx_len = 0; // reset message
return false;
}
if (mp3_rx_data[2] < mp3_rx_len - 2U - 2U) { // message is not complete
return false;
}
if (0xef != mp3_rx_data[3 + mp3_rx_data[2]]) { // end by is not correct
return false;
}
/*
// display message
puts("> ");
for (uint8_t i = 0; i < mp3_rx_len; i++) {
printf("%02x ", mp3_rx_data[i]);
}
putc('\n');
*/
// check checksum
int16_t checksum = 0;
for (uint8_t i = 1; i < mp3_rx_len && i < mp3_rx_data[2] + 1U; i++) {
checksum += mp3_rx_data[i];
}
checksum = -checksum;
const int16_t expected = ((mp3_rx_data[mp3_rx_data[2] + 1] << 8) + mp3_rx_data[mp3_rx_data[2] + 2]);
if (expected != checksum) {
printf("wrong checksum: should=%+04x is=%+04x\n", expected, checksum);
mp3_rx_len = 0; // reset message
return false;
}
switch (mp3_rx_data[3]) {
case 0x3a:
puts("card inserted");
break;
case 0x3b:
puts("card removed");
break;
case 0x3d:
puts("track finished");
break;
case 0x40:
puts("error");
break;
case 0x41:
puts("ack");
break;
case 0x4e:
puts("track count");
break;
default:
printf("unknown: %+02x", mp3_rx_data[3]);
break;
}
putc('\n');
mp3_rx_len = 0; // reset message
return true;
}
/** send command to MP3 playes
* @param[in] cmd command to send
* @param[in] data argument for command (such as track number)
*/
static void mp3_command(enum mp3_commands_t cmd, uint16_t data)
{
puts("MP3 command: ");
switch (cmd) {
case MP3_CMD_PLAY:
puts("play");
break;
case MP3_CMD_NEXT_SONG:
puts("next");
break;
case MP3_CMD_RESET:
puts("reset");
break;
default:
printf("%+02x");
break;
}
putc(' ');
uint8_t command[] = {0x7e, 0xff, 0x06, cmd, 0x01, data >> 8, data, 0xef}; // command template (with feedback)
for (uint8_t i = 0; i < LENGTH(command); i++) {
usart_send_blocking(USART(MP3_UART), command[i]);
}
}
/** play MP3
* @param[in] argument no argument required
*/
static void command_play(void* argument)
{
(void)argument; // argument not used
puts("sending play command\n");
mp3_command(MP3_CMD_PLAY, 0);
}
/** open/close door
* @param[in] argument no argument required
*/
static void command_door(void* argument)
{
(void)argument; // argument not used
if (0 == timer_door_closed) {
puts("closing door\n");
timer_door_closed = rtc_get_counter_val();
led_tm1637_time(0, 0);
led_tm1637_on();
mp3_command(MP3_CMD_PLAY, 0);
} else {
puts("opening door\n");
timer_door_closed = 0;
led_tm1637_off();
mp3_command(MP3_CMD_STOP_PLAY, 0); // stop playing
}
}
/** list of all supported commands */
static const struct menu_command_t menu_commands[] = {
{
@ -174,6 +349,22 @@ static const struct menu_command_t menu_commands[] = {
.argument_description = NULL,
.command_handler = &command_bootloader,
},
{
.shortcut = 'p',
.name = "play",
.command_description = "play MP3 song",
.argument = MENU_ARGUMENT_NONE,
.argument_description = NULL,
.command_handler = &command_play,
},
{
.shortcut = 'd',
.name = "door",
.command_description = "open/close door",
.argument = MENU_ARGUMENT_NONE,
.argument_description = NULL,
.command_handler = &command_door,
},
};
static void command_help(void* argument)
@ -273,6 +464,19 @@ static void process_command(char* str)
}
}
/** switch LED sign to besetzt/occupied
* @param[in] besetzt if the sign should light up besetzt/occupied (true) or frei/free(false)
*/
static void leds_sign(bool besetzt)
{
for (uint8_t led = 0; led < LED_SK6812RGBW_LEDS / 2; led++) {
led_sk6812rgbw_set_rgb(led, 0, besetzt ? 0 : 0xff, 0, 0);
}
for (uint8_t led = LED_SK6812RGBW_LEDS / 2; led < LED_SK6812RGBW_LEDS; led++) {
led_sk6812rgbw_set_rgb(led, besetzt ? 0xff : 0 , 0, 0, 0);
}
}
/** program entry point
* this is the firmware function started by the micro-controller
*/
@ -352,6 +556,64 @@ void main(void)
time_start = rtc_get_counter_val(); // get start time from internal RTC
puts("OK\n");
puts("setup TM1637 7-segment display: ");
bool led_tm1637_setup_ok = true;
led_tm1637_setup();
led_tm1637_setup_ok &= led_tm1637_brightness(LED_TM1637_14DIV16); // set maximum brightess
led_tm1637_setup_ok &= led_tm1637_time(88, 88); // light up all segments
led_tm1637_setup_ok &= led_tm1637_on(); // switch on to test it
sleep_ms(1000); // let used check display
led_tm1637_setup_ok &= led_tm1637_off(); // switch off and let main loop handle it
if (led_tm1637_setup_ok) {
puts("OK\n");
} else {
puts("error\n");
}
// setup LEDs
puts("setup SK6812RGBW LEDs: ");
led_sk6812rgbw_setup();
for (uint8_t led = 0; led < LED_SK6812RGBW_LEDS; led++) {
led_sk6812rgbw_set_rgb(led, 0, 0, 0, 64); // switch white on
}
sleep_ms(1000); // give user time to verify
for (uint8_t led = 0; led < LED_SK6812RGBW_LEDS; led++) {
led_sk6812rgbw_set_rgb(led, 0, 0, 0, 0); // switch all off
}
leds_sign(0 != timer_door_closed);
puts("OK\n");
puts("setup catalex YX5300 MP3 player: ");
rcc_periph_clock_enable(RCC_USART_PORT(MP3_UART)); // enable clock for UART port peripheral
rcc_periph_clock_enable(RCC_USART(MP3_UART)); // enable clock for UART peripheral
rcc_periph_reset_pulse(RST_USART(MP3_UART)); // reset peripheral
rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (UART)
gpio_set_mode(USART_TX_PORT(MP3_UART), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_TX_PIN(MP3_UART)); // setup GPIO pin UART transmit
gpio_set_mode(USART_RX_PORT(MP3_UART), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_RX_PIN(MP3_UART)); // setup GPIO pin UART receive
gpio_set(USART_RX_PORT(MP3_UART), USART_RX_PIN(MP3_UART)); // pull up to avoid noise when not connected
usart_set_baudrate(USART(MP3_UART), 9600);
usart_set_databits(USART(MP3_UART), 8);
usart_set_stopbits(USART(MP3_UART), USART_STOPBITS_1);
usart_set_mode(USART(MP3_UART), USART_MODE_TX_RX);
usart_set_parity(USART(MP3_UART), USART_PARITY_NONE);
usart_set_flow_control(USART(MP3_UART), USART_FLOWCONTROL_NONE);
usart_enable_rx_interrupt(USART(MP3_UART)); // enable receive interrupt
nvic_enable_irq(USART_IRQ(MP3_UART)); // enable the UART interrupt
usart_enable(USART(MP3_UART)); // enable UART
puts("OK\n");
mp3_command(MP3_CMD_RESET, 0); // reset all settings
puts("setup door lock switch: ");
rcc_periph_clock_enable(GPIO_RCC(DOOR_PIN)); // enable clock for button
gpio_set_mode(GPIO_PORT(DOOR_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(DOOR_PIN)); // set button pin to input
rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
exti_select_source(GPIO_EXTI(DOOR_PIN), GPIO_PORT(DOOR_PIN)); // mask external interrupt of this pin only for this port
gpio_set(GPIO_PORT(DOOR_PIN), GPIO_PIN(DOOR_PIN)); // pull up to be able to detect button push (go low)
exti_set_trigger(GPIO_EXTI(DOOR_PIN), EXTI_TRIGGER_BOTH); // trigger on change
exti_enable_request(GPIO_EXTI(DOOR_PIN)); // enable external interrupt
nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(DOOR_PIN)); // enable interrupt
puts("OK\n");
// setup terminal
terminal_prefix = ""; // set default prefix
terminal_process = &process_command; // set central function to process commands
@ -368,19 +630,44 @@ void main(void)
char c = user_input_get(); // store receive character
terminal_send(c); // send received character to terminal
}
if (button_flag) { // user pressed button
action = true; // action has been performed
puts("button pressed\n");
led_toggle(); // toggle LED
sleep_ms(100); // wait a bit to remove noise and double trigger
button_flag = false; // reset flag
}
if (rtc_internal_tick_flag) { // the internal RTC ticked
rtc_internal_tick_flag = false; // reset flag
action = true; // action has been performed
if (0 == (rtc_get_counter_val() % RTC_TICKS_SECOND)) { // one seond has passed
if (0 == (rtc_get_counter_val() % RTC_TICKS_SECOND)) { // one second has passed
led_toggle(); // toggle LED (good to indicate if main function is stuck)
}
if (timer_door_closed && 0 == ((rtc_get_counter_val() - timer_door_closed) % RTC_TICKS_SECOND)) { // on second has passed since since door closed
uint16_t time_passed = (rtc_get_counter_val() - timer_door_closed) / RTC_TICKS_SECOND; // in seconds
led_tm1637_time(time_passed / 60, time_passed % 60);
if (time_passed && 0 == (time_passed % 5)) {
mp3_command(MP3_CMD_NEXT_SONG, 0);
}
}
}
if (door_flag) { // door switch state changed
sleep_ms(100); // wait a bit to de-noise before we check the door lock state
const bool closed = (0 == gpio_get(GPIO_PORT(DOOR_PIN), GPIO_PIN(DOOR_PIN))); // get door lock state
door_flag = false; // clear flag
action = true; // action has been performed
if (closed && 0 == timer_door_closed) {
puts("door closed\n");
timer_door_closed = rtc_get_counter_val();
leds_sign(true);
led_tm1637_time(0, 0);
led_tm1637_on();
mp3_command(MP3_CMD_PLAY, 0);
} else if (!closed && timer_door_closed) {
puts("door opned\n");
timer_door_closed = 0;
leds_sign(false);
led_tm1637_off();
mp3_command(MP3_CMD_STOP_PLAY, 0); // stop playing
}
}
if (mp3_rx_flag) { // data from MP3 player
mp3_response(); // check for response
mp3_rx_flag = false; // clear flag
action = true; // action has been performed
}
if (action) { // go to sleep if nothing had to be done, else recheck for activity
action = false;
@ -396,3 +683,22 @@ void rtc_isr(void)
rtc_clear_flag(RTC_SEC); // clear flag
rtc_internal_tick_flag = true; // notify to show new time
}
/** UART interrupt service routine called when data has been received */
void USART_ISR(MP3_UART)(void)
{
if (usart_get_flag(USART(MP3_UART), USART_SR_RXNE)) { // data has been transmitted
if (mp3_rx_len < LENGTH(mp3_rx_data)) {
mp3_rx_data[mp3_rx_len++] = usart_recv(USART(MP3_UART)); // store received data
} else {
usart_recv(USART(MP3_UART)); // discard data
}
mp3_rx_flag = true; // notify user
}
}
void GPIO_EXTI_ISR(DOOR_PIN)(void)
{
exti_reset_request(GPIO_EXTI(DOOR_PIN)); // reset interrupt/clear flag
door_flag = true; // notify main loop
}