stm32f1/lib/vfd_hv518.c

476 lines
18 KiB
C

/** library to drive vacuum fluorescent display using supertex HV518 shift register VFD drivers
* @details the current configuration is for a VFD extracted from a Samsung SER-6500 cash register
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2016-2020
* @note peripherals used: SPI @ref vfd_hv518_spi , GPIO @ref vfd_hv518_gpio , timer @ref vfd_hv518_timer
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/spi.h> // SPI library
#include <libopencm3/stm32/timer.h> // timer library
#include <libopencm3/cm3/nvic.h> // interrupt handler
#include "global.h" // global definitions
#include "vfd_hv518.h" // VFD library API
/** @defgroup vfd_hv518_gpio GPIO to control supertex HV518 VFD drivers
* @{
*/
#define VFD_STR_PIN PA6 /**< strobe pin to enable high voltage output, high voltage is output on low */
#define VFD_NLE_PIN PA4 /**< latch enable pin, stores the shifted data on low, output the parallel data on high */
/** @} */
/** @defgroup vfd_hv518_spi SPI to send data to supertex HV518 VFD drivers
* @{
*/
#define VFD_SPI 1 /**< SPI peripheral */
/** @} */
/** @defgroup vfd_hv518_timer timer for automatic display blocks refresh
* @{
*/
#define VFD_TIMER 2 /**< timer peripheral ID */
/** @} */
/** ASCII characters encoded for the 7 segments digit block
* @note starts with space
*/
static const uint8_t ascii_7segments[] = {
0x00, // 0b00000000, space
0x30, // 0b00110000, ! (I)
0x22, // 0b00100010, "
0x5c, // 0b01011100, # (o)
0x6d, // 0b01101101, $ (s)
0x52, // 0b01010010, % (/)
0x7d, // 0b01111101, & (6)
0x20, // 0b00100000, '
0x39, // 0b00111001, ( ([)
0x0f, // 0b00001111, )
0x70, // 0b01110000, *
0x46, // 0b01000110, +
0x10, // 0b00010000, ,
0x40, // 0b01000000, -
0x10, // 0b00010000, . (,)
0x52, // 0b01010010, /
0x3f, // 0b00111111, 0
0x06, // 0b00000110, 1
0x5b, // 0b01011011, 2
0x4f, // 0b01001111, 3
0x66, // 0b01100110, 4
0x6d, // 0b01101101, 5
0x7d, // 0b01111101, 6
0x07, // 0b00000111, 7
0x7f, // 0b01111111, 8
0x6f, // 0b01101111, 9
0x48, // 0b01001000, : (=)
0x48, // 0b01001000, ; (=)
0x58, // 0b01011000, <
0x48, // 0b01001000, =
0x4c, // 0b01001100, >
0x53, // 0b01010011, ?
0x7b, // 0b01111011, @
0x77, // 0b01110111, A
0x7f, // 0b01111111, B
0x39, // 0b00111001, C
0x5e, // 0b01011110, D
0x79, // 0b01111001, E
0x71, // 0b01110001, F
0x3d, // 0b00111101, G
0x76, // 0b01110110, H
0x30, // 0b00110000, I
0x1e, // 0b00011110, J
0x76, // 0b01110110, K
0x38, // 0b00111000, L
0x37, // 0b00110111, M
0x37, // 0b00110111, N
0x3f, // 0b00111111, O
0x73, // 0b01110011, P
0x6b, // 0b01101011, Q
0x33, // 0b00110011, R
0x6d, // 0b01101101, S
0x78, // 0b01111000, T
0x3e, // 0b00111110, U
0x3e, // 0b00111110, V (U)
0x3e, // 0b00111110, W (U)
0x76, // 0b01110110, X (H)
0x6e, // 0b01101110, Y
0x5b, // 0b01011011, Z
0x39, // 0b00111001, [
0x64, // 0b01100100, '\'
0x0f, // 0b00001111, /
0x23, // 0b00100011, ^
0x08, // 0b00001000, _
0x02, // 0b00000010, `
0x5f, // 0b01011111, a
0x7c, // 0b01111100, b
0x58, // 0b01011000, c
0x5e, // 0b01011110, d
0x7b, // 0b01111011, e
0x71, // 0b01110001, f
0x6f, // 0b01101111, g
0x74, // 0b01110100, h
0x10, // 0b00010000, i
0x0c, // 0b00001100, j
0x76, // 0b01110110, k
0x30, // 0b00110000, l
0x54, // 0b01010100, m
0x54, // 0b01010100, n
0x5c, // 0b01011100, o
0x73, // 0b01110011, p
0x67, // 0b01100111, q
0x50, // 0b01010000, r
0x6d, // 0b01101101, s
0x78, // 0b01111000, t
0x1c, // 0b00011100, u
0x1c, // 0b00011100, v (u)
0x1c, // 0b00011100, w (u)
0x76, // 0b01110110, x
0x6e, // 0b01101110, y
0x5b, // 0b01011011, z
0x39, // 0b00111001, { ([)
0x30, // 0b00110000, |
0x0f, // 0b00001111, } ([)
0x40, // 0b01000000, ~
};
/** font for the 5x7 dot matrix block
* @details first value is left-most line, LSB is top dot, MSB is not used
* @note from http://sunge.awardspace.com/glcd-sd/node4.html
*/
static const uint8_t font5x7[][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // (space)
{0x00, 0x00, 0x5F, 0x00, 0x00}, // !
{0x00, 0x07, 0x00, 0x07, 0x00}, // "
{0x14, 0x7F, 0x14, 0x7F, 0x14}, // #
{0x24, 0x2A, 0x7F, 0x2A, 0x12}, // $
{0x23, 0x13, 0x08, 0x64, 0x62}, // %
{0x36, 0x49, 0x55, 0x22, 0x50}, // &
{0x00, 0x05, 0x03, 0x00, 0x00}, // '
{0x00, 0x1C, 0x22, 0x41, 0x00}, // (
{0x00, 0x41, 0x22, 0x1C, 0x00}, // )
{0x08, 0x2A, 0x1C, 0x2A, 0x08}, // *
{0x08, 0x08, 0x3E, 0x08, 0x08}, // +
{0x00, 0x50, 0x30, 0x00, 0x00}, // ,
{0x08, 0x08, 0x08, 0x08, 0x08}, // -
{0x00, 0x60, 0x60, 0x00, 0x00}, // .
{0x20, 0x10, 0x08, 0x04, 0x02}, // /
{0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0
{0x00, 0x42, 0x7F, 0x40, 0x00}, // 1
{0x42, 0x61, 0x51, 0x49, 0x46}, // 2
{0x21, 0x41, 0x45, 0x4B, 0x31}, // 3
{0x18, 0x14, 0x12, 0x7F, 0x10}, // 4
{0x27, 0x45, 0x45, 0x45, 0x39}, // 5
{0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6
{0x01, 0x71, 0x09, 0x05, 0x03}, // 7
{0x36, 0x49, 0x49, 0x49, 0x36}, // 8
{0x06, 0x49, 0x49, 0x29, 0x1E}, // 9
{0x00, 0x36, 0x36, 0x00, 0x00}, // :
{0x00, 0x56, 0x36, 0x00, 0x00}, // ;
{0x00, 0x08, 0x14, 0x22, 0x41}, // <
{0x14, 0x14, 0x14, 0x14, 0x14}, // =
{0x41, 0x22, 0x14, 0x08, 0x00}, // >
{0x02, 0x01, 0x51, 0x09, 0x06}, // ?
{0x32, 0x49, 0x79, 0x41, 0x3E}, // @
{0x7E, 0x11, 0x11, 0x11, 0x7E}, // A
{0x7F, 0x49, 0x49, 0x49, 0x36}, // B
{0x3E, 0x41, 0x41, 0x41, 0x22}, // C
{0x7F, 0x41, 0x41, 0x22, 0x1C}, // D
{0x7F, 0x49, 0x49, 0x49, 0x41}, // E
{0x7F, 0x09, 0x09, 0x01, 0x01}, // F
{0x3E, 0x41, 0x41, 0x51, 0x32}, // G
{0x7F, 0x08, 0x08, 0x08, 0x7F}, // H
{0x00, 0x41, 0x7F, 0x41, 0x00}, // I
{0x20, 0x40, 0x41, 0x3F, 0x01}, // J
{0x7F, 0x08, 0x14, 0x22, 0x41}, // K
{0x7F, 0x40, 0x40, 0x40, 0x40}, // L
{0x7F, 0x02, 0x04, 0x02, 0x7F}, // M
{0x7F, 0x04, 0x08, 0x10, 0x7F}, // N
{0x3E, 0x41, 0x41, 0x41, 0x3E}, // O
{0x7F, 0x09, 0x09, 0x09, 0x06}, // P
{0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q
{0x7F, 0x09, 0x19, 0x29, 0x46}, // R
{0x46, 0x49, 0x49, 0x49, 0x31}, // S
{0x01, 0x01, 0x7F, 0x01, 0x01}, // T
{0x3F, 0x40, 0x40, 0x40, 0x3F}, // U
{0x1F, 0x20, 0x40, 0x20, 0x1F}, // V
{0x7F, 0x20, 0x18, 0x20, 0x7F}, // W
{0x63, 0x14, 0x08, 0x14, 0x63}, // X
{0x03, 0x04, 0x78, 0x04, 0x03}, // Y
{0x61, 0x51, 0x49, 0x45, 0x43}, // Z
{0x00, 0x00, 0x7F, 0x41, 0x41}, // [
{0x02, 0x04, 0x08, 0x10, 0x20}, // '\'
{0x41, 0x41, 0x7F, 0x00, 0x00}, // ]
{0x04, 0x02, 0x01, 0x02, 0x04}, // ^
{0x40, 0x40, 0x40, 0x40, 0x40}, // _
{0x00, 0x01, 0x02, 0x04, 0x00}, // `
{0x20, 0x54, 0x54, 0x54, 0x78}, // a
{0x7F, 0x48, 0x44, 0x44, 0x38}, // b
{0x38, 0x44, 0x44, 0x44, 0x20}, // c
{0x38, 0x44, 0x44, 0x48, 0x7F}, // d
{0x38, 0x54, 0x54, 0x54, 0x18}, // e
{0x08, 0x7E, 0x09, 0x01, 0x02}, // f
{0x08, 0x14, 0x54, 0x54, 0x3C}, // g
{0x7F, 0x08, 0x04, 0x04, 0x78}, // h
{0x00, 0x44, 0x7D, 0x40, 0x00}, // i
{0x20, 0x40, 0x44, 0x3D, 0x00}, // j
{0x00, 0x7F, 0x10, 0x28, 0x44}, // k
{0x00, 0x41, 0x7F, 0x40, 0x00}, // l
{0x7C, 0x04, 0x18, 0x04, 0x78}, // m
{0x7C, 0x08, 0x04, 0x04, 0x78}, // n
{0x38, 0x44, 0x44, 0x44, 0x38}, // o
{0x7C, 0x14, 0x14, 0x14, 0x08}, // p
{0x08, 0x14, 0x14, 0x18, 0x7C}, // q
{0x7C, 0x08, 0x04, 0x04, 0x08}, // r
{0x48, 0x54, 0x54, 0x54, 0x20}, // s
{0x04, 0x3F, 0x44, 0x40, 0x20}, // t
{0x3C, 0x40, 0x40, 0x20, 0x7C}, // u
{0x1C, 0x20, 0x40, 0x20, 0x1C}, // v
{0x3C, 0x40, 0x30, 0x40, 0x3C}, // w
{0x44, 0x28, 0x10, 0x28, 0x44}, // x
{0x0C, 0x50, 0x50, 0x50, 0x3C}, // y
{0x44, 0x64, 0x54, 0x4C, 0x44}, // z
{0x00, 0x08, 0x36, 0x41, 0x00}, // {
{0x00, 0x00, 0x7F, 0x00, 0x00}, // |
{0x00, 0x41, 0x36, 0x08, 0x00}, // }
{0x08, 0x04, 0x0c, 0x08, 0x04}, // ~ {0b00001000, 0b00000100, 0b00001100, 0b00001000, 0b00000100}
};
/** pictures for the 5x7 dot matrix block
* @details first value is left-most line, LSB is top dot, MSB is not used
*/
static const uint8_t pict5x7[][5] = {
{0x08, 0x08, 0x2A, 0x1C, 0x08}, // ->
{0x08, 0x1C, 0x2A, 0x08, 0x08}, // <-
{0x70, 0x70, 0x7a, 0x7c, 0x58}, // bunny side 1 {0b01110000, 0b01110000, 0b01111010, 0b01111100, 0b01011000}
{0x20, 0x70, 0x72, 0x7c, 0x58}, // bunny side 2 {0b00100000, 0b01110000, 0b01110010, 0b01111100, 0b01011000}
{0x3e, 0x49, 0x56, 0x49, 0x3e}, // bunny face 1 {0b00111110, 0b01001001, 0b01010110, 0b01001001, 0b00111110}
{0x3e, 0x51, 0x66, 0x51, 0x3e}, // bunny face 2 {0b00111110, 0b01010001, 0b01100110, 0b01010001, 0b00111110}
{0x38, 0x57, 0x64, 0x57, 0x38}, // bunny face 3 {0b00111000, 0b01010111, 0b01100100, 0b01010111, 0b00111000}
{0x38, 0x4f, 0x54, 0x4f, 0x38}, // bunny face 4 {0b00111000, 0b01001111, 0b01010100, 0b01001111, 0b00111000}
{0x38, 0x5e, 0x68, 0x5e, 0x38}, // bunny face 5 {0b00111000, 0b01011110, 0b01101000, 0b01011110, 0b00111000}
{0x41, 0x36, 0x08, 0x36, 0x41}, // cross 1 {0b01000001, 0b00110110, 0b00001000, 0b00110110, 0b01000001}
{~0x41, ~0x36, ~0x08, ~0x36, ~0x41}, // cross 1 negated {~0b01000001, ~0b00110110, ~0b00001000, ~0b00110110, ~0b01000001}
{0x22, 0x14, 0x08, 0x14, 0x22}, // cross 2 {0b00100010, 0b00010100, 0b00001000, 0b00010100, 0b00100010}
{~0x22, ~0x14, ~0x08, ~0x14, ~0x22}, // cross 2 negated {~0b00100010, ~0b00010100, ~0b00001000, ~0b00010100, ~0b00100010}
{0x00, 0x00, 0x00, 0x00, 0x00}, // nothing
};
/** the 32 bits values to be shifted out to the VFD driver
* @note split into 16 bit for SPI transfer
* @note since the bits for digits and matrix are independent, they can be combined
* @note we have more matrix (12) than digits (10)
*/
static uint16_t driver_data[VFD_MATRIX][VFD_DRIVERS * 2] = {0};
/** which driver data is being transmitted */
static volatile uint8_t spi_i = 0;
/** which grid/part to activate
* @note digits and matrix can be combined
*/
static volatile uint8_t vfd_grid = 0;
/** the bits used for selecting then digit and 7 segment anodes
* @note for the second driver
*/
static const uint32_t digit_mask = 0x00fffff0;
void vfd_digit(uint8_t nb, char c)
{
if (!(nb < VFD_DIGITS)) { // check the digit exists
return;
}
uint32_t digit_data = 0; // the data to be shifted out for the driver (for the second driver)
digit_data = 1 << (4 + (9 - nb)); // select digit
/* encode segment
* here the bit order (classic 7 segment + underline and dot)
* 3_
* 8|9_|4
* 7|6_|5.1
* 0_2,
* */
if (false) { // add the underline (not encoded)
digit_data |= (1 << 14);
}
if (c&0x80) { // add the dot (encoded in the 8th bit)
digit_data |= (1 << 15);
}
if (false) { // add the comma (not encoded)
digit_data |= (1 << 16);
}
c &= 0x7f; // only take the ASCII part
if (c >= ' ') { // only take printable characters
uint8_t i = c - ' '; // get index for character
if (i < LENGTH(ascii_7segments)) {
digit_data |= (ascii_7segments[i] << 17); // add encoded segments to memory
}
}
digit_data &= digit_mask; // be sure only the bits for the digit are used
digit_data |= (driver_data[nb][2] + (driver_data[nb][3] << 16)) & ~digit_mask; // get the existing data and add the bits for the digit
driver_data[nb][2] = digit_data; // write back data (least significant half)
driver_data[nb][3] = (digit_data >> 16); // write back data (most significant half)
}
void vfd_matrix(uint8_t nb, char c)
{
// check the matrix exists
if (!(nb < VFD_MATRIX)) {
return;
}
uint32_t matrix_data[VFD_DRIVERS] = {0}; // the data to be shifted out for the driver
// select matrix
if (nb < 4) {
matrix_data[1] = 1 << (3 - nb);
} else {
matrix_data[0] = 1 << (35 - nb);
}
if ((c < 0x80) && (c >= ' ')) { // only take printable characters
uint8_t i = c - ' '; // get index for character
if (i < LENGTH(font5x7)) {
matrix_data[1] |= font5x7[i][0] << 24;
matrix_data[2] |= font5x7[i][1] << 0;
matrix_data[2] |= font5x7[i][2] << 8;
matrix_data[2] |= font5x7[i][3] << 16;
matrix_data[2] |= font5x7[i][4] << 24;
}
} else if (c > 0x7f) { // the non ASCII character are used for pictures
uint8_t i = c - 0x80; // get index for character
if (i < LENGTH(pict5x7)) {
matrix_data[1] |= pict5x7[i][0] << 24;
matrix_data[2] |= pict5x7[i][1] << 0;
matrix_data[2] |= pict5x7[i][2] << 8;
matrix_data[2] |= pict5x7[i][3] << 16;
matrix_data[2] |= pict5x7[i][4] << 24;
}
}
matrix_data[1] &= ~digit_mask; // be sure only the bits for the matrix are used
matrix_data[1] |= (driver_data[nb][2] + (driver_data[nb][3] << 16)) & digit_mask; // get the existing data for the digit
// prepare the data for SPI to shift it out
for (uint8_t i = 0; i < LENGTH(matrix_data); i++) {
driver_data[nb][i * 2] = matrix_data[i];
driver_data[nb][i * 2 + 1] = matrix_data[i] >> 16;
}
}
void vfd_clear(void)
{
for (uint8_t i = 0; i < LENGTH(driver_data); i++) {
for (uint8_t j = 0; j < LENGTH(driver_data[0]); j++) {
driver_data[i][j] = 0;
}
}
}
void vfd_test(void)
{
for (uint8_t i = 0; i < LENGTH(driver_data); i++) {
for (uint8_t j = 0; j < LENGTH(driver_data[0]); j++) {
driver_data[i][j] = ~0;
}
}
}
void vfd_on(void)
{
gpio_clear(GPIO_PORT(VFD_STR_PIN), GPIO_PIN(VFD_STR_PIN)); // enable HV output
timer_enable_counter(TIM(VFD_TIMER)); // start timer to periodically output that to the parts
}
void vfd_off(void)
{
gpio_set(GPIO_PORT(VFD_STR_PIN), GPIO_PIN(VFD_STR_PIN)); // disable HV output
timer_disable_counter(TIM(VFD_TIMER)); // stop timer to periodically output that to the parts
}
void vfd_setup(void)
{
/* setup GPIO to control the VFD */
rcc_periph_clock_enable(GPIO_RCC(VFD_STR_PIN)); // enable clock for VFD GPIO
gpio_set(GPIO_PORT(VFD_STR_PIN), GPIO_PIN(VFD_STR_PIN)); // disable HV output
gpio_set_mode(GPIO_PORT(VFD_STR_PIN), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(VFD_STR_PIN)); // set VFD pin to output push-pull
rcc_periph_clock_enable(GPIO_RCC(VFD_NLE_PIN)); // enable clock for VFD GPIO
gpio_clear(GPIO_PORT(VFD_NLE_PIN), GPIO_PIN(VFD_NLE_PIN)); // do not output latched data
gpio_set_mode(GPIO_PORT(VFD_NLE_PIN), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(VFD_NLE_PIN)); // set VFD pin to output push-pull
/* setup SPI to transmit data */
rcc_periph_clock_enable(RCC_SPI(VFD_SPI)); // enable SPI clock
rcc_periph_clock_enable(RCC_SPI_SCK_PORT(VFD_SPI)); // enable clock for VFD SPI GPIO
rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(VFD_SPI)); // enable clock for VFD SPI GPIO
gpio_set_mode(SPI_SCK_PORT(VFD_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(VFD_SPI)); // set VFD pin to alternative function push-pull
gpio_set_mode(SPI_MOSI_PORT(VFD_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(VFD_SPI)); // set VFD pin to alternative function push-pull
spi_reset(SPI(VFD_SPI)); // clear SPI values
/* set SPI:
* - use VFD_SPI port
* - divide clock by 8 for generating the baudrate (F_PCLK1 is 36MHz, max HV518 is 6MHz)
* - clock idle high polarity
* - data is valid on rising edge (second clock phase)
* - send 16 bits at a time
* - send least significant bit first (that's how I coded the data)
*/
spi_init_master(SPI(VFD_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_8, SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_2, SPI_CR1_DFF_16BIT, SPI_CR1_LSBFIRST);
//spi_set_bidirectional_transmit_only_mode(VFD_SPI); // only use MOSI to transmit
spi_set_unidirectional_mode(SPI(VFD_SPI)); // MISO is unused
/* set NSS high to enable transmission
* the NSS in STM32 can not be used as hardware slave select
* RM0008 reference manual 25.3.1 is misleading
* when hardware NSS is used and output is enabled NSS never goes up after transmission, even if SPI is disabled
* when software NSS is used, NSS can not be set high again, even when writing to the register
* the slave select must be done manually using GPIO */
spi_enable_software_slave_management(SPI(VFD_SPI));
spi_set_nss_high(SPI(VFD_SPI)); // set NSS high
nvic_enable_irq(SPI_IRQ(VFD_SPI)); // enable SPI interrupt
spi_enable(SPI(VFD_SPI)); // enable SPI (the tx empty interrupt will trigger)
/* setup timer to refresh display */
rcc_periph_clock_enable(RCC_TIM(VFD_TIMER)); // enable clock for timer block
rcc_periph_reset_pulse(RST_TIM(VFD_TIMER)); // reset timer state
timer_set_mode(TIM(VFD_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up
timer_set_prescaler(TIM(VFD_TIMER), (rcc_ahb_frequency / (1 << 16)) - 1); // set the prescaler so this 16 bits timer overflows at 1Hz
timer_set_period(TIM(VFD_TIMER), 0xffff / LENGTH(driver_data) / 100); // set the refresh frequency
timer_enable_irq(TIM(VFD_TIMER), TIM_DIER_UIE); // enable interrupt for timer
nvic_enable_irq(NVIC_TIM_IRQ(VFD_TIMER)); // allow interrupt for timer
vfd_clear(); // initialize values
}
/** SPI interrupt service routine called when data has been transmitted */
void SPI_ISR(VFD_SPI)(void)
{
if (SPI_SR(SPI(VFD_SPI)) & SPI_SR_TXE) { // transmission buffer empty
if (spi_i < LENGTH(driver_data[0])) { // check if data is available
gpio_clear(GPIO_PORT(VFD_NLE_PIN), GPIO_PIN(VFD_NLE_PIN)); // slave select to latch data
spi_send(SPI(VFD_SPI), driver_data[vfd_grid][spi_i++]); // send next data
} else { // all data transmitted
spi_disable_tx_buffer_empty_interrupt(SPI(VFD_SPI)); // no need to wait for new data
while (SPI_SR(SPI(VFD_SPI)) & SPI_SR_BSY); // wait for data to be shifted out
spi_disable_tx_buffer_empty_interrupt(SPI(VFD_SPI)); // no need to wait for new data
gpio_set(GPIO_PORT(VFD_NLE_PIN), GPIO_PIN(VFD_NLE_PIN)); // output latched data
}
}
}
/** timer interrupt service routine called time passed */
void TIM_ISR(VFD_TIMER)(void)
{
if (timer_get_flag(TIM(VFD_TIMER), TIM_SR_UIF)) { // overflow even happened
timer_clear_flag(TIM(VFD_TIMER), TIM_SR_UIF); // clear flag
spi_i = 0; // set the register to shift out
spi_enable_tx_buffer_empty_interrupt(SPI(VFD_SPI)); // enable TX empty interrupt
vfd_grid = (vfd_grid + 1) % LENGTH(driver_data); // got to next segment
}
}