#include "stm32f401xe.h"
#include "timer.h"
#include <stdint.h>
#include <stdbool.h>
#include <stm32f4xx.h>
#include "exti.h"
#include "pin.h"
#include "registers.h"
#include "display.h"
#include "delay.h"
#include "uart.h"
#include "spi.h"
#include "buffered_peripheral.h"
#include "spi_matrix.h"
void hard_fault_handler() {
while(1) {}
}
void usage_fault_handler() {
while(1) {}
}
void nmi_handler() {
while(1) {}
}
void bus_fault_handler() {
while(1) {}
}
/*----------------------------------------------------------------------------
* SystemCoreClockConfigure: configure SystemCoreClock using HSI
(HSE is not populated on Nucleo board)
*----------------------------------------------------------------------------*/
void SystemCoreClockSetHSI(void) {
RCC->CR |= ((uint32_t)RCC_CR_HSION); // Enable HSI
while ((RCC->CR & RCC_CR_HSIRDY) == 0); // Wait for HSI Ready
RCC->CFGR = RCC_CFGR_SW_HSI; // HSI is system clock
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI); // Wait for HSI used as system clock
FLASH->ACR = FLASH_ACR_PRFTEN; // Enable Prefetch Buffer
FLASH->ACR |= FLASH_ACR_ICEN; // Instruction cache enable
FLASH->ACR |= FLASH_ACR_DCEN; // Data cache enable
FLASH->ACR |= FLASH_ACR_LATENCY_5WS; // Flash 5 wait state
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // HCLK = SYSCLK
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 = HCLK/4
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 = HCLK/2
RCC->CR &= ~RCC_CR_PLLON; // Disable PLL
// HSI = 16 MHz
// PLL configuration: VCO = HSI/M * N, Sysclk = VCO/P
// => Sysclk = 48 MHz, APB1 = 12 MHz, APB2 = 24 MHz
// Since divider for APB1 is != 1, timer clock is 24 MHz
RCC->PLLCFGR = ( 16ul | // PLL_M = 16
(384ul << 6) | // PLL_N = 384
( 3ul << 16) | // PLL_P = 8
(RCC_PLLCFGR_PLLSRC_HSI) | // PLL_SRC = HSI
( 8ul << 24) ); // PLL_Q = 8
RCC->CR |= RCC_CR_PLLON; // Enable PLL
while((RCC->CR & RCC_CR_PLLRDY) == 0) __NOP(); // Wait till PLL is ready
RCC->CFGR &= ~RCC_CFGR_SW; // Select PLL as system clock source
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait till PLL is system clock src
}
#define COMMAND_SEPARATOR '\n'
// Button
pin_t user_button;
timer_t button_timer;
exti_t button_exti;
pin_t led;
// Display
matrix_t matrix;
timer_t matrix_timer;
// SPI
pin_t spi_csn;
spi_t matrix_spi;
buffered_transceiver_t matrix_tx;
timer_t spi_csn_timer;
// Uart
uart_t uart;
buffered_transceiver_t uart_rx;
#define MAX_CMD_LENGTH 65
#define AUTO_TOGGLE_CYCLES 1000000
#define MAX_IMAGES 10
#define COLS 8
#define ROWS 8
uint8_t images_count = 4;
uint8_t current_image = 0;
uint8_t images[MAX_IMAGES][8] = {
{0b11111111, 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b11111111},
{ 0b00000100, 0b00001110, 0b11110001, 0b10000101, 0b10000001, 0b10110001, 0b10110001, 0b11111111 },
{ 0b11111000, 0b11100100, 0b11100010, 0b10000001, 0b10000001, 0b01000010, 0b00100100, 0b00011000 },
{ 0b11000000, 0b10011000, 0b11010000, 0b10011010, 0b10010010, 0b00011010, 0b00000010, 0b00000011 },
};
uint32_t cycle = 0;
bool auto_toggle = false;
bool toggle_next = false;
bool animation = false;
void num_to_string(uint16_t num, char* res, uint16_t digits) {
for (uint16_t i = 0; i < digits; i++) {
res[digits - 1 - i] = num % 10 + '0';
num /= 10;
}
}
void handle_command(char* cmd, uint16_t len) {
if (len == 0) {
return;
}
bool handled = false;
switch (cmd[0]) {
case 'n': // next
if (len == 1) {
handled = true;
toggle_next = true;
buffered_transceiver_transmit(&uart_rx, "Switching to next image.\r\n", 0);
}
break;
case 'N': // auto toggle
if (len == 1) {
handled = true;
auto_toggle = !auto_toggle;
if (auto_toggle) {
buffered_transceiver_transmit(&uart_rx, "Going to toggle automatically.\r\n", 0);
} else {
buffered_transceiver_transmit(&uart_rx, "Manual switch mode.\r\n", 0);
}
}
break;
case 'a': // animate
if (len == 1) {
handled = true;
animation = !animation;
if (animation) {
buffered_transceiver_transmit(&uart_rx, "Animation enabled.\r\n", 0);
} else {
buffered_transceiver_transmit(&uart_rx, "Animation disabled.\r\n", 0);
}
}
break;
case 'u': // upload
// upload
// TODO
break;
case 's': // number of slots is...
if (len == 1) {
handled = true;
buffered_transceiver_transmit(&uart_rx, "Used slots: ", 0);
char str[5] = { '\0', '\0', '\r', '\n', '\0' };
num_to_string(images_count, str, 2);
buffered_transceiver_transmit(&uart_rx, str, 0);
}
break;
case 'I': // increase intensity
if (len == 1) {
matrix_increase_intensity(&matrix);
buffered_transceiver_transmit(&uart_rx, "Increasing intensity.\r\n", 0);
handled = true;
}
break;
case 'i': // decrease intensity
if (len == 1) {
matrix_decrease_intensity(&matrix);
buffered_transceiver_transmit(&uart_rx, "Decreasing intensity.\r\n", 0);
handled = true;
}
break;
case 'h':
buffered_transceiver_transmit(&uart_rx, "Help:\r\n", 0);
buffered_transceiver_transmit(&uart_rx, " h - show this help messages\r\n", 0);
buffered_transceiver_transmit(&uart_rx, " s - print used slots\r\n", 0);
buffered_transceiver_transmit(&uart_rx, " a - toggle animating\r\n", 0);
buffered_transceiver_transmit(&uart_rx, " n - next image\r\n", 0);
buffered_transceiver_transmit(&uart_rx, " N - automatically switch images\r\n", 0);
buffered_transceiver_transmit(&uart_rx, " I - increase intensity\r\n", 0);
buffered_transceiver_transmit(&uart_rx, " i - decrease intensity\r\n", 0);
buffered_transceiver_transmit(&uart_rx, " uIMG - upload new image, IMG has \"X\" for 1s and \"-\" for 0s. It should be 64 chars long\r\n", 0);
handled = true;
break;
default:
break;
}
if (!handled) {
buffered_transceiver_transmit(&uart_rx, "Unknown command!\r\n", 0);
}
}
char* receive_command(uint16_t* length)
{
static char cmd[MAX_CMD_LENGTH];
static uint16_t index;
char* current_char = (cmd + index);
while (buffered_transceiver_receive(&uart_rx, current_char, 1) == 1) {
// echo
buffered_transceiver_transmit(&uart_rx, current_char, 1);
if (*current_char == COMMAND_SEPARATOR) {
buffered_transceiver_transmit(&uart_rx, "\r", 1);
*current_char = '\0';
*length = index;
index = 0;
return cmd;
}
index++;
}
// Commands are separated either through max length, or by new line
if (index == MAX_CMD_LENGTH + 1) {
*length = MAX_CMD_LENGTH;
index = 0;
return cmd;
}
return NULL;
}
void next_image()
{
if (matrix.state != MATRIX_STABLE) {
return;
}
current_image++;
current_image %= images_count;
matrix_set_buffer(&matrix, MATRIX_SLOT_OTHER, &images[current_image][0]);
if (animation) {
matrix_animate_swap(&matrix);
} else {
matrix_swap(&matrix);
}
}
void app_loop()
{
while (1) {
if (auto_toggle) {
pin_set(&led);
} else {
pin_reset(&led);
}
if (auto_toggle && cycle >= AUTO_TOGGLE_CYCLES) {
next_image();
cycle = 0;
} else if (toggle_next) {
next_image();
toggle_next = false;
}
uint16_t command_len;
char* cmd = receive_command(&command_len);
if (cmd != NULL) {
handle_command(cmd, command_len);
}
cycle++;
}
}
void main()
{
// Setup
SystemCoreClockSetHSI();
systick_configure();
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM4EN | RCC_APB1ENR_USART2EN | RCC_APB1ENR_TIM2EN;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2LPENR_SPI1LPEN;
{ // UART init
pin_t pin_rx;
pin_t pin_tx;
#define UART_RX_BUFFER_SIZE 64
#define UART_TX_BUFFER_SIZE 300
static queue_t uart_rx_queue, uart_tx_queue;
static uint16_t uart_rx_buffer[UART_RX_BUFFER_SIZE], uart_tx_buffer[UART_TX_BUFFER_SIZE];
queue_init(&uart_rx_queue, sizeof(char), UART_RX_BUFFER_SIZE, uart_rx_buffer);
queue_init(&uart_tx_queue, sizeof(char), UART_TX_BUFFER_SIZE, uart_tx_buffer);
bool usart_generic_can_receive(void* peripheral);
bool usart_generic_can_transmit(void* peripheral);
uint16_t usart_generic_receive(void* peripheral, void* buffer, uint16_t max_size);
uint16_t usart_generic_transmit(void* peripheral, void* data, uint16_t size);
buffered_transceiver_vtable_t vtable = {
.can_receive = usart_generic_can_receive,
.can_transmit = usart_generic_can_transmit,
.transmit = usart_generic_transmit,
.receive = usart_generic_receive,
};
buffered_transceiver_init(&uart_rx, &uart_rx_queue, &uart_tx_queue, &uart, vtable);
pin_init(&pin_tx, GPIOA, 2);
pin_init(&pin_rx, GPIOA, 3);
pin_into_alternate(&pin_rx, 7);
pin_into_alternate(&pin_tx, 7);
usart_init(&uart, USART2, 2);
usart_configure_speed(&uart, 12000000, 9600);
usart_configure_uart(&uart, true, USART_WORD_9_BITS, true,
USART_PARITY_EVEN, USART_STOP_BIT_ONE);
usart_enable_interrupt(&uart, false, true);
usart_configure_transmitter(&uart, true);
usart_configure_receiver(&uart, true);
}
{ // SPI init
pin_t pin_mosi;
pin_t pin_csn;
pin_t pin_clk;
#define SPI_RX_BUFFER_SIZE 0
#define SPI_TX_BUFFER_SIZE 10
static queue_t spi_rx_queue, spi_tx_queue;
static uint16_t spi_rx_buffer[SPI_RX_BUFFER_SIZE], spi_tx_buffer[SPI_TX_BUFFER_SIZE];
queue_init(&spi_rx_queue, sizeof(uint16_t), SPI_RX_BUFFER_SIZE, spi_rx_buffer);
queue_init(&spi_tx_queue, sizeof(uint16_t), SPI_TX_BUFFER_SIZE, spi_tx_buffer);
bool spi_generic_can_receive(void* peripheral);
bool spi_generic_can_transmit(void* peripheral);
uint16_t spi_generic_receive(void* peripheral, void* buffer, uint16_t max_size);
uint16_t spi_generic_transmit(void* peripheral, void* data, uint16_t size);
buffered_transceiver_vtable_t vtable = {
.can_receive = spi_generic_can_receive,
.can_transmit = spi_generic_can_transmit,
.transmit = spi_generic_transmit,
.receive = spi_generic_receive,
};
buffered_transceiver_init(&matrix_tx, &spi_rx_queue, &spi_tx_queue, &matrix_spi, vtable);
pin_init(&pin_mosi, GPIOA, 7);
pin_init(&pin_csn, GPIOA, 4);
pin_init(&pin_clk, GPIOB, 3);
pin_into_alternate(&pin_mosi, 5);
/* pin_into_alternate(&pin_csn, 5); */
pin_into_alternate(&pin_clk, 5);
pin_set(&pin_csn);
pin_into_output_pushpull(&pin_csn);
spi_init(&matrix_spi, pin_csn, SPI1, 1);
spi_master_configure_speed(&matrix_spi, 7);
spi_master_configure(&matrix_spi, false, false, false,
SPI_MSB_FIRST, SPI_FRAME_16_BIT);
spi_enable_interrupt(&matrix_spi, false, true);
spi_master_enable(&matrix_spi, true);
}
{ // Matrix, timer init
matrix_init(&matrix, &matrix_tx, 5);
matrix_setup(&matrix);
matrix_enable(&matrix, true);
matrix_set_buffer(&matrix, MATRIX_SLOT0, images[current_image]);
timer_init(&matrix_timer, TIM3, 3);
timer_set_refresh(&matrix_timer, 20);
timer_configure(&matrix_timer, 0, 60000, 0);
timer_enable_interrupt(&matrix_timer, true, false);
timer_enable(&matrix_timer);
}
{ // Button, exti, timer
pin_init(&user_button, GPIOC, 13);
pin_into_input(&user_button);
pin_init(&led, GPIOA, 5);
pin_into_output(&led);
exti_init(&button_exti, 13, EXTI, SYSCFG);
exti_external_interrupt(&button_exti, 2);
exti_both_edges_interrupt(&button_exti);
exti_clear_interrupt(&button_exti);
exti_enable_interrupt(&button_exti);
timer_init(&button_timer, TIM2, 2);
timer_set_refresh(&button_timer, 400);
timer_configure(&button_timer, 0, 60000, 1);
timer_enable_interrupt(&button_timer, true, false);
}
__enable_irq();
// Application
app_loop();
}
void EXTI15_10_handler(void) {
if (exti_is_interrupt(&button_exti)) {
exti_clear_interrupt(&button_exti);
bool button_pressed = !pin_read(&user_button);
if (button_pressed) { // start timer on button press
timer_set_counter(&button_timer, 0);
timer_clear_update_interrupt(&button_timer);
timer_enable(&button_timer);
// handle short presses, prevent registering bouncing
} else if (timer_count(&button_timer) > 10) {
timer_disable(&button_timer);
timer_set_counter(&button_timer, 0);
// short press (under 1 s)
if (auto_toggle) {
auto_toggle = false;
} else {
toggle_next = true;
}
}
}
}
void TIM2_handler(void) {
if (timer_is_update_interrupt(&button_timer)) {
timer_clear_update_interrupt(&button_timer);
// long press (over 1 s)
auto_toggle = !auto_toggle;
cycle = 0;
}
}
void TIM3_handler(void) {
timer_clear_update_interrupt(&matrix_timer);
matrix_update(&matrix, &matrix_tx);
}
// A flag to prevent spi communication,
// to toggle csn high
bool spi_can_transmit_flag = true;
void SPI1_handler(void) {
// using can receive as not busy interrupt
if (spi_can_receive(&matrix_spi)) {
uint16_t tmp;
spi_receive(&matrix_spi, &tmp, 1);
spi_pulse_csn(&matrix_spi);
spi_can_transmit_flag = true;
buffered_transceiver_trigger_transmit(&matrix_tx, 1);
}
}
void USART2_handler(void) {
if (usart_can_receive(&uart)) {
buffered_transceiver_trigger_receive(&uart_rx, 1);
}
if (usart_can_transmit(&uart)) {
if (!buffered_transceiver_trigger_transmit(&uart_rx, 1)) {
usart_enable_interrupt(&uart, false, true);
}
}
}
bool spi_generic_can_receive(void *peripheral) {
return spi_can_receive((spi_t*)peripheral);
}
bool spi_generic_can_transmit(void *peripheral) {
return spi_can_transmit_flag && (((spi_t*)peripheral)->periph->SR & SPI_SR_BSY) == 0;
}
uint16_t spi_generic_receive(void *peripheral, void *buffer,
uint16_t max_size) {
return spi_receive((spi_t*)peripheral, buffer, max_size);
}
uint16_t spi_generic_transmit(void *peripheral, void *data, uint16_t size) {
// enable the interrupt so that we can send the rest
uint16_t ret = spi_transmit((spi_t*)peripheral, data, size);
spi_can_transmit_flag = false;
return ret;
}
bool usart_generic_can_receive(void *peripheral) {
return usart_can_receive((uart_t*)peripheral);
}
bool usart_generic_can_transmit(void *peripheral) {
return usart_can_transmit((uart_t*)peripheral);
}
uint16_t usart_generic_receive(void *peripheral, void *buffer,
uint16_t max_size) {
return usart_receive((uart_t*)peripheral, buffer, max_size);
}
uint16_t usart_generic_transmit(void *peripheral, void *data, uint16_t size) {
uint16_t ret = usart_transmit((uart_t*)peripheral, data, size);
// enable the interrupt so that we can send the rest
usart_enable_interrupt((uart_t*)peripheral, true, true);
return ret;
}