parent
fc7f31f033
commit
4fb61d1ea9
@ -0,0 +1,129 @@ |
|||||||
|
#include <string.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#include <utils.h> |
||||||
|
#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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
#ifndef MATRIXDSP_H |
||||||
|
#define MATRIXDSP_H |
||||||
|
|
||||||
|
#include "stm32f1xx_hal.h" |
||||||
|
#include "max2719.h" |
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
|
||||||
|
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
|
@ -0,0 +1,38 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <inttypes.h> |
||||||
|
#include <user_main.h> |
||||||
|
#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; |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
#ifndef MALLOC_SAFE_H |
||||||
|
#define MALLOC_SAFE_H |
||||||
|
|
||||||
|
/**
|
||||||
|
* Malloc that prints error and restarts the system on failure. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdint.h> |
||||||
|
#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
|
@ -0,0 +1,81 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include <spi.h> |
||||||
|
#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); |
||||||
|
} |
@ -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
|
Loading…
Reference in new issue