#include "stm32f401xe.h" #include "timer.h" #include #include #include #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; }