diff --git a/CMakeLists.txt b/CMakeLists.txt index 3299ead..cf2c27b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ project(f107-fft C ASM) cmake_minimum_required(VERSION 3.5.0) -file(GLOB_RECURSE USER_SOURCES "Src/*.c") +file(GLOB_RECURSE USER_SOURCES "Src/*.c" "Max2719/*.c") file(GLOB_RECURSE HAL_SOURCES "Drivers/STM32F1xx_HAL_Driver/Src/*.c") add_library(HAL ${HAL_SOURCES}) @@ -13,12 +13,16 @@ include_directories(Inc) include_directories(Drivers/STM32F1xx_HAL_Driver/Inc) include_directories(Drivers/CMSIS/Include) include_directories(Drivers/CMSIS/Device/ST/STM32F1xx/Include) +include_directories(Max2719) +include_directories(/usr/arm-none-eabi/include) +include_directories(/usr/lib/gcc/arm-none-eabi/6.1.1/include/) + add_definitions(-DSTM32F107xC) add_definitions(-DUSE_FULL_ASSERT) add_executable(${PROJECT_NAME}.elf ${USER_SOURCES} ${LINKER_SCRIPT}) -target_link_libraries(${PROJECT_NAME}.elf HAL CMSIS) +target_link_libraries(${PROJECT_NAME}.elf HAL CMSIS ) set(HEX_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.hex) set(BIN_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.bin) diff --git a/Inc/user_main.h b/Inc/user_main.h index 8eb6e0f..343f4eb 100644 --- a/Inc/user_main.h +++ b/Inc/user_main.h @@ -11,4 +11,6 @@ void user_Error_Handler(); void user_assert_failed(uint8_t* file, uint32_t line); +void user_error_file_line(const char *message, const char *file, uint32_t line); + #endif //F107_FFT_USER_MAIN_H diff --git a/Max2719/dotmatrix.c b/Max2719/dotmatrix.c new file mode 100644 index 0000000..fe848e4 --- /dev/null +++ b/Max2719/dotmatrix.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include "dotmatrix.h" +#include "malloc_safe.h" + +DotMatrix_Cfg* dmtx_init(DotMatrix_Init *init) +{ + DotMatrix_Cfg *disp = calloc_s(1, sizeof(DotMatrix_Cfg)); + + disp->drv.SPIx = init->SPIx; + disp->drv.CS_GPIOx = init->CS_GPIOx; + disp->drv.CS_PINx = init->CS_PINx; + disp->drv.chain_len = init->cols * init->rows; + disp->cols = init->cols; + disp->rows = init->rows; + + disp->screen = calloc_s(init->cols * init->rows * 8, 1); // 8 bytes per driver + + max2719_cmd_all(&disp->drv, MAX2719_CMD_DECODE_MODE, 0x00); // no decode + max2719_cmd_all(&disp->drv, MAX2719_CMD_SCAN_LIMIT, 0x07); // scan all 8 + max2719_cmd_all(&disp->drv, MAX2719_CMD_SHUTDOWN, 0x01); // not shutdown + max2719_cmd_all(&disp->drv, MAX2719_CMD_DISPLAY_TEST, 0x00); // not test + max2719_cmd_all(&disp->drv, MAX2719_CMD_INTENSITY, 0x07); // half intensity + + // clear + for (uint8_t i = 0; i < 8; i++) { + max2719_cmd_all(&disp->drv, MAX2719_CMD_DIGIT0+i, 0); + } + + return disp; +} + +void dmtx_show(DotMatrix_Cfg* disp) +{ + for (uint8_t i = 0; i < 8; i++) { + // show each digit's array in turn + max2719_cmd_all_data(&disp->drv, MAX2719_CMD_DIGIT0+i, disp->screen + (i * disp->drv.chain_len)); + } +} + +void dmtx_clear(DotMatrix_Cfg* disp) +{ + memset(disp->screen, 0, disp->drv.chain_len*8); +} + +void dmtx_intensity(DotMatrix_Cfg* disp, uint8_t intensity) +{ + max2719_cmd_all(&disp->drv, MAX2719_CMD_INTENSITY, intensity & 0x0F); +} + +void dmtx_blank(DotMatrix_Cfg* disp, bool blank) +{ + max2719_cmd_all(&disp->drv, MAX2719_CMD_SHUTDOWN, (!blank) & 0x01); +} + +/** + * @brief Get a cell pointer + * @param disp : driver inst + * @param x : x coord + * @param y : y coord + * @param xd : pointer to store the offset in the cell + * @return cell ptr + */ +static uint8_t* cell_ptr(DotMatrix_Cfg* disp, int32_t x, int32_t y, uint8_t *xd) +{ + if (x < 0 || y < 0) return NULL; + if ((uint32_t)x >= disp->cols*8 || (uint32_t)y >= disp->rows*8) return NULL; + + uint32_t cell_x = (uint32_t)x >> 3; + *xd = x & 7; + + // resolve cell + uint32_t digit = y & 7; + cell_x += ((uint32_t)y >> 3) * disp->cols; + + uint32_t cell_idx = (digit * disp->drv.chain_len) + cell_x; + + return &disp->screen[cell_idx]; +} + + +bool dmtx_get(DotMatrix_Cfg* disp, int32_t x, int32_t y) +{ + uint8_t xd; + uint8_t *cell = cell_ptr(disp, x, y, &xd); + if (cell == NULL) return 0; + + return (*cell >> xd) & 1; +} + + +void dmtx_toggle(DotMatrix_Cfg* disp, int32_t x, int32_t y) +{ + uint8_t xd; + uint8_t *cell = cell_ptr(disp, x, y, &xd); + if (cell == NULL) return; + + *cell ^= 1 << xd; +} + + +void dmtx_set(DotMatrix_Cfg* disp, int32_t x, int32_t y, bool bit) +{ + uint8_t xd; + uint8_t *cell = cell_ptr(disp, x, y, &xd); + if (cell == NULL) return; + + if (bit) { + *cell |= 1 << xd; + } else { + *cell &= ~(1 << xd); + } +} + +void dmtx_set_block(DotMatrix_Cfg* disp, int32_t startX, int32_t startY, uint32_t *data_rows, uint32_t width, uint16_t height) +{ + for (uint32_t y = 0; y < height; y++) { + uint32_t row = data_rows[y]; + + for (uint32_t x = 0; x < width; x++) { + int xx = startX + (int)x; + int yy = startY + (int)y; + bool val = (row >> (width - x - 1)) & 1; + + dmtx_set(disp, xx, yy, val); + } + } +} diff --git a/Max2719/dotmatrix.h b/Max2719/dotmatrix.h new file mode 100644 index 0000000..b61c7eb --- /dev/null +++ b/Max2719/dotmatrix.h @@ -0,0 +1,63 @@ +#ifndef MATRIXDSP_H +#define MATRIXDSP_H + +#include "stm32f1xx_hal.h" +#include "max2719.h" +#include + + +typedef struct { + MAX2719_Cfg drv; + uint8_t *screen; /*!< Screen array, organized as series of [all #1 digits], [all #2 digits] ... */ + uint32_t cols; /*!< Number of drivers horizontally */ + uint32_t rows; /*!< Number of drivers vertically */ +} DotMatrix_Cfg; + +typedef struct { + SPI_TypeDef *SPIx; /*!< SPI iface used by this instance */ + GPIO_TypeDef *CS_GPIOx; /*!< Chip select GPIO port */ + uint16_t CS_PINx; /*!< Chip select pin mask */ + uint32_t cols; /*!< Number of drivers horizontally */ + uint32_t rows; /*!< Number of drivers vertically */ +} DotMatrix_Init; + +// global inst +extern DotMatrix_Cfg *dmtx; + + +DotMatrix_Cfg* dmtx_init(DotMatrix_Init *init); + +/** + * @brief Display the whole screen array + * @param dmtx : driver struct + */ +void dmtx_show(DotMatrix_Cfg* disp); + +/** Set intensity 0-16 */ +void dmtx_intensity(DotMatrix_Cfg* disp, uint8_t intensity); + +/** Display on/off */ +void dmtx_blank(DotMatrix_Cfg* disp, bool blank); + +/** + * @brief Send a single bit + * @param dmtx : driver struct + * @param x : pixel X + * @param y : pixel Y + * @param bit : 1 or 0 + */ +void dmtx_set(DotMatrix_Cfg* disp, int32_t x, int32_t y, bool bit); + +/** Get a single bit */ +bool dmtx_get(DotMatrix_Cfg* disp, int32_t x, int32_t y); + +/** Set a block using array of row data */ +void dmtx_set_block(DotMatrix_Cfg* disp, int32_t startX, int32_t startY, uint32_t *data_rows, uint32_t width, uint16_t height); + +/** Toggle a single bit */ +void dmtx_toggle(DotMatrix_Cfg* disp, int32_t x, int32_t y); + +/** Clear the screen (not showing) */ +void dmtx_clear(DotMatrix_Cfg* disp); + +#endif // MATRIXDSP_H diff --git a/Max2719/malloc_safe.c b/Max2719/malloc_safe.c new file mode 100644 index 0000000..1e9036c --- /dev/null +++ b/Max2719/malloc_safe.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include "malloc_safe.h" + +static void reset_when_done(void) +{ + // + + HAL_NVIC_SystemReset(); +} + + +void *malloc_safe_do(size_t size, const char* file, uint32_t line) +{ + void *mem = malloc(size); + if (mem == NULL) { + // malloc failed + user_error_file_line("Malloc failed", file, line); + reset_when_done(); + } + + return mem; +} + + +void *calloc_safe_do(size_t nmemb, size_t size, const char* file, uint32_t line) +{ + void *mem = calloc(nmemb, size); + if (mem == NULL) { + // malloc failed + user_error_file_line("Calloc failed", file, line); + reset_when_done(); + } + + return mem; +} diff --git a/Max2719/malloc_safe.h b/Max2719/malloc_safe.h new file mode 100644 index 0000000..b5abec1 --- /dev/null +++ b/Max2719/malloc_safe.h @@ -0,0 +1,18 @@ +#ifndef MALLOC_SAFE_H +#define MALLOC_SAFE_H + +/** + * Malloc that prints error and restarts the system on failure. + */ + +#include +#include +#include "stm32f1xx_hal.h" + +void *malloc_safe_do(size_t size, const char* file, uint32_t line); +void *calloc_safe_do(size_t nmemb, size_t size, const char* file, uint32_t line); + +#define malloc_s(size) malloc_safe_do(size, __FILE__, __LINE__) +#define calloc_s(nmemb, size) calloc_safe_do(nmemb, size, __FILE__, __LINE__) + +#endif // MALLOC_SAFE_H diff --git a/Max2719/max2719.c b/Max2719/max2719.c new file mode 100644 index 0000000..4efa15e --- /dev/null +++ b/Max2719/max2719.c @@ -0,0 +1,81 @@ +#include +#include +#include "max2719.h" + +// TODO convert to buffer + batch write. +// TODO store hspi in the max2719 struct, so it can be used. + +static inline +void send_byte(MAX2719_Cfg *inst, uint8_t b) +{ + //inst->SPIx->DR = b; + //while (!(inst->SPIx->SR & SPI_SR_TXE)); + + // FIXME figure out why regular transmit is not working + HAL_SPI_Transmit(&hspi1, &b, 1, 10); +} + + +static inline +void set_nss(MAX2719_Cfg *inst, bool nss) +{ + if (nss) { + inst->CS_GPIOx->BSRR = inst->CS_PINx; + } else { + inst->CS_GPIOx->BRR = inst->CS_PINx; + } +} + + +static void send_word(MAX2719_Cfg *inst, MAX2719_Command cmd, uint8_t data) +{ + send_byte(inst, cmd); + send_byte(inst, data); +} + + +void max2719_cmd(MAX2719_Cfg *inst, uint32_t nth, MAX2719_Command cmd, uint8_t data) +{ + set_nss(inst, 0); + while (inst->SPIx->SR & SPI_SR_BSY); + + for (uint32_t i = 0; i < inst->chain_len; i++) { + if (i == inst->chain_len - nth - 1) { + send_word(inst, cmd, data); + } else { + send_word(inst, MAX2719_CMD_NOOP, 0); + } + } + + while (inst->SPIx->SR & SPI_SR_BSY); + set_nss(inst, 1); +} + + + +void max2719_cmd_all(MAX2719_Cfg *inst, MAX2719_Command cmd, uint8_t data) +{ + set_nss(inst, 0); + while (inst->SPIx->SR & SPI_SR_BSY); + + for (uint32_t i = 0; i < inst->chain_len; i++) { + send_word(inst, cmd, data); + } + + while (inst->SPIx->SR & SPI_SR_BSY); + set_nss(inst, 1); +} + + +void max2719_cmd_all_data(MAX2719_Cfg *inst, MAX2719_Command cmd, uint8_t *data) +{ + set_nss(inst, 0); + while (inst->SPIx->SR & SPI_SR_BSY); + + for (uint32_t i = 0; i < inst->chain_len; i++) { + send_word(inst, cmd, data[inst->chain_len - i - 1]); + } + + while (inst->SPIx->SR & SPI_SR_BSY); + set_nss(inst, 1); +} diff --git a/Max2719/max2719.h b/Max2719/max2719.h new file mode 100644 index 0000000..373e94e --- /dev/null +++ b/Max2719/max2719.h @@ -0,0 +1,61 @@ +#ifndef MAX2719_H +#define MAX2719_H + +#include "stm32f1xx_hal.h" + +/** Generic utilities for controlling the MAX2719 display driver */ + +typedef struct { + SPI_TypeDef *SPIx; /*!< SPI iface used by this instance */ + GPIO_TypeDef *CS_GPIOx; /*!< Chip select GPIO port */ + uint16_t CS_PINx; /*!< Chip select pin mask */ + uint32_t chain_len; /*!< Number of daisy-chained drivers (for "all" or "n-th" commands */ +} MAX2719_Cfg; + + +typedef enum { + MAX2719_CMD_NOOP = 0x00, + + MAX2719_CMD_DIGIT0 = 0x01, + MAX2719_CMD_DIGIT1 = 0x02, + MAX2719_CMD_DIGIT2 = 0x03, + MAX2719_CMD_DIGIT3 = 0x04, + MAX2719_CMD_DIGIT4 = 0x05, + MAX2719_CMD_DIGIT5 = 0x06, + MAX2719_CMD_DIGIT6 = 0x07, + MAX2719_CMD_DIGIT7 = 0x08, + + MAX2719_CMD_DECODE_MODE = 0x09, + MAX2719_CMD_INTENSITY = 0x0A, + MAX2719_CMD_SCAN_LIMIT = 0x0B, + MAX2719_CMD_SHUTDOWN = 0x0C, + MAX2719_CMD_DISPLAY_TEST = 0x0F, +} MAX2719_Command; + + +/** + * @brief Send a command to a single driver + * @param inst : config struct + * @param nth : driver index + * @param cmd : command + * @param data : data byte + */ +void max2719_cmd(MAX2719_Cfg *inst, uint32_t nth, MAX2719_Command cmd, uint8_t data); + +/** + * @brief Send command to all drivers, with the same data + * @param inst : config struct + * @param cmd : command + * @param data : data byte + */ +void max2719_cmd_all(MAX2719_Cfg *inst, MAX2719_Command cmd, uint8_t data); + +/** + * @brief Send command to all drivers, with varying data + * @param inst : config struct + * @param cmd : command + * @param data : array of data bytes (must be long to cover all drivers) + */ +void max2719_cmd_all_data(MAX2719_Cfg *inst, MAX2719_Command cmd, uint8_t *data); + +#endif // MAX2719_H diff --git a/Src/stm32f1xx_it.c b/Src/stm32f1xx_it.c index 2c8b0aa..55b4251 100644 --- a/Src/stm32f1xx_it.c +++ b/Src/stm32f1xx_it.c @@ -31,6 +31,7 @@ ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ +#include #include "stm32f1xx_hal.h" #include "stm32f1xx.h" #include "stm32f1xx_it.h" @@ -65,6 +66,7 @@ void NMI_Handler(void) void HardFault_Handler(void) { /* USER CODE BEGIN HardFault_IRQn 0 */ + uart_print("Hard fault.\n"); /* USER CODE END HardFault_IRQn 0 */ while (1) @@ -81,6 +83,7 @@ void HardFault_Handler(void) void MemManage_Handler(void) { /* USER CODE BEGIN MemoryManagement_IRQn 0 */ + uart_print("MemManage fault.\n"); /* USER CODE END MemoryManagement_IRQn 0 */ while (1) @@ -97,6 +100,7 @@ void MemManage_Handler(void) void BusFault_Handler(void) { /* USER CODE BEGIN BusFault_IRQn 0 */ + uart_print("Bus fault.\n"); /* USER CODE END BusFault_IRQn 0 */ while (1) @@ -113,6 +117,7 @@ void BusFault_Handler(void) void UsageFault_Handler(void) { /* USER CODE BEGIN UsageFault_IRQn 0 */ + uart_print("Usage fault.\n"); /* USER CODE END UsageFault_IRQn 0 */ while (1) diff --git a/Src/tim.c b/Src/tim.c index 2d7f21a..fce6ce9 100644 --- a/Src/tim.c +++ b/Src/tim.c @@ -50,7 +50,7 @@ void MX_TIM3_Init(void) htim3.Instance = TIM3; htim3.Init.Prescaler = 0; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; - htim3.Init.Period = 1800; + htim3.Init.Period = 6000;//1800 htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_OC_Init(&htim3) != HAL_OK) { diff --git a/Src/user_main.c b/Src/user_main.c index b5600c0..e50ae25 100644 --- a/Src/user_main.c +++ b/Src/user_main.c @@ -4,6 +4,7 @@ #include #include +#include #include "mxconstants.h" #include "stm32f1xx_hal.h" #include "utils.h" @@ -13,23 +14,37 @@ static uint32_t audio_samples[256]; +static DotMatrix_Cfg *disp; + void start_DMA() { - uart_print("- Starting ADC DMA\n"); + //uart_print("- Starting ADC DMA\n"); - HAL_ADC_Start_DMA(&hadc1, audio_samples, 256); + HAL_ADC_Start_DMA(&hadc1, audio_samples, 32); HAL_TIM_Base_Start(&htim3); } /** This callback is called by HAL after the transfer is complete */ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { - uart_print("- DMA complete.\n"); +// uart_print("- DMA complete.\n"); - char x[100]; - sprintf(x, "%"PRIu32"\n", audio_samples[0]); - uart_print(x); +// char x[100]; +// +// for (int i = 0; i < 256; i++) { +// sprintf(x, "%"PRIu32" ", audio_samples[i]); +// uart_print(x); +// } +// uart_print("\n"); + + dmtx_clear(disp); + for (int i = 0; i < 32; i++) { + dmtx_set(disp, i, ((audio_samples[i])>>6)-24, 1); + } + dmtx_show(disp); } void user_main() { + uart_print("== USER CODE STARTING ==\n"); + // Leds OFF HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1); HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, 1); @@ -39,13 +54,29 @@ void user_main() { // Enable audio input HAL_GPIO_WritePin(AUDIO_NSTBY_GPIO_Port, AUDIO_NSTBY_Pin, 1); + DotMatrix_Init disp_init; + disp_init.cols = 4; + disp_init.rows = 2; + disp_init.CS_GPIOx = SPI1_CS_GPIO_Port; + disp_init.CS_PINx = SPI1_CS_Pin; + disp_init.SPIx = SPI1; + disp = dmtx_init(&disp_init); + + dmtx_intensity(disp, 2); + + dmtx_clear(disp); + dmtx_show(disp); + while (1) { // Blink HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); - HAL_Delay(500); + HAL_Delay(50); - uart_print("Main loop\n"); + //uart_print("Main loop\n"); start_DMA(); + + //dmtx_toggle(disp, 31, 15); + //dmtx_show(disp); } } @@ -64,7 +95,12 @@ void user_Error_Handler() { * @retval None */ void user_assert_failed(uint8_t *file, uint32_t line) { - uart_print("Assert failed in file "); + user_error_file_line("Assert failed", (const char *) file, line); +} + +void user_error_file_line(const char *message, const char *file, uint32_t line) { + uart_print(message); + uart_print(" in file "); uart_print((char *) file); uart_print(" on line ");