2020-02-17 18:04:38 +01:00
/** library to drive vacuum fluorescent display using supertex HV518 shift register VFD drivers
2016-08-14 20:18:10 +02:00
* @ details the current configuration is for a VFD extracted from a Samsung SER - 6500 cash register
2020-01-03 00:16:59 +01:00
* @ file
2016-08-14 20:18:10 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-02-17 18:04:38 +01:00
* @ date 2016 - 2020
2016-08-14 20:18:10 +02:00
* @ note peripherals used : SPI @ ref vfd_hv518_spi , GPIO @ ref vfd_hv518_gpio , timer @ ref vfd_hv518_timer
*/
2016-01-28 21:21:50 +01:00
/* 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
2016-08-14 20:18:10 +02:00
/** @defgroup vfd_hv518_gpio GPIO to control supertex HV518 VFD drivers
* @ {
*/
2020-02-17 18:04:38 +01:00
# 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 */
2016-08-14 20:18:10 +02:00
/** @} */
/** @defgroup vfd_hv518_spi SPI to send data to supertex HV518 VFD drivers
* @ {
*/
2020-02-17 18:04:38 +01:00
# define VFD_SPI 1 /**< SPI peripheral */
2016-08-14 20:18:10 +02:00
/** @} */
2016-01-28 21:21:50 +01:00
2016-08-14 20:18:10 +02:00
/** @defgroup vfd_hv518_timer timer for automatic display blocks refresh
* @ {
*/
2020-02-17 18:04:38 +01:00
# define VFD_TIMER 2 /**< timer peripheral ID */
2016-08-14 20:18:10 +02:00
/** @} */
/** ASCII characters encoded for the 7 segments digit block
* @ note starts with space
2016-01-28 21:21:50 +01:00
*/
static const uint8_t ascii_7segments [ ] = {
2020-02-17 18:04:38 +01:00
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, ~
2016-01-28 21:21:50 +01:00
} ;
2016-08-14 20:18:10 +02:00
/** 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
2016-01-28 21:21:50 +01:00
*/
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 } , // }
2020-02-17 18:04:38 +01:00
{ 0x08 , 0x04 , 0x0c , 0x08 , 0x04 } , // ~ {0b00001000, 0b00000100, 0b00001100, 0b00001000, 0b00000100}
2016-01-28 21:21:50 +01:00
} ;
2016-08-14 20:18:10 +02:00
/** pictures for the 5x7 dot matrix block
* @ details first value is left - most line , LSB is top dot , MSB is not used
2016-01-28 21:21:50 +01:00
*/
static const uint8_t pict5x7 [ ] [ 5 ] = {
{ 0x08 , 0x08 , 0x2A , 0x1C , 0x08 } , // ->
{ 0x08 , 0x1C , 0x2A , 0x08 , 0x08 } , // <-
2020-02-17 18:04:38 +01:00
{ 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
2016-01-28 21:21:50 +01:00
} ;
2016-08-14 20:18:10 +02:00
/** 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 )
2016-01-28 21:21:50 +01:00
*/
2020-02-17 18:04:38 +01:00
static uint16_t driver_data [ VFD_MATRIX ] [ VFD_DRIVERS * 2 ] = { 0 } ;
2016-08-14 20:18:10 +02:00
/** which driver data is being transmitted */
static volatile uint8_t spi_i = 0 ;
2019-03-26 19:27:40 +01:00
/** which grid/part to activate
2016-08-14 20:18:10 +02:00
* @ 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 ;
2016-01-28 21:21:50 +01:00
void vfd_digit ( uint8_t nb , char c )
{
2020-02-17 18:04:38 +01:00
if ( ! ( nb < VFD_DIGITS ) ) { // check the digit exists
2016-01-28 21:21:50 +01:00
return ;
}
uint32_t digit_data = 0 ; // the data to be shifted out for the driver (for the second driver)
2019-03-26 19:27:40 +01:00
2020-02-17 18:04:38 +01:00
digit_data = 1 < < ( 4 + ( 9 - nb ) ) ; // select digit
2016-01-28 21:21:50 +01:00
/* 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)
2020-02-17 18:04:38 +01:00
digit_data | = ( 1 < < 14 ) ;
2016-01-28 21:21:50 +01:00
}
if ( c & 0x80 ) { // add the dot (encoded in the 8th bit)
2020-02-17 18:04:38 +01:00
digit_data | = ( 1 < < 15 ) ;
2016-01-28 21:21:50 +01:00
}
if ( false ) { // add the comma (not encoded)
2020-02-17 18:04:38 +01:00
digit_data | = ( 1 < < 16 ) ;
2016-01-28 21:21:50 +01:00
}
2019-03-26 19:27:40 +01:00
2016-01-28 21:21:50 +01:00
c & = 0x7f ; // only take the ASCII part
2020-02-17 18:04:38 +01:00
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
2016-01-28 21:21:50 +01:00
}
}
digit_data & = digit_mask ; // be sure only the bits for the digit are used
2020-02-17 18:04:38 +01:00
digit_data | = ( driver_data [ nb ] [ 2 ] + ( driver_data [ nb ] [ 3 ] < < 16 ) ) & ~ digit_mask ; // get the existing data and add the bits for the digit
2016-01-28 21:21:50 +01:00
driver_data [ nb ] [ 2 ] = digit_data ; // write back data (least significant half)
2020-02-17 18:04:38 +01:00
driver_data [ nb ] [ 3 ] = ( digit_data > > 16 ) ; // write back data (most significant half)
2016-01-28 21:21:50 +01:00
}
void vfd_matrix ( uint8_t nb , char c )
{
// check the matrix exists
2020-02-17 18:04:38 +01:00
if ( ! ( nb < VFD_MATRIX ) ) {
2016-01-28 21:21:50 +01:00
return ;
2019-03-26 19:27:40 +01:00
}
2016-01-28 21:21:50 +01:00
uint32_t matrix_data [ VFD_DRIVERS ] = { 0 } ; // the data to be shifted out for the driver
2019-03-26 19:27:40 +01:00
2016-01-28 21:21:50 +01:00
// select matrix
2020-02-17 18:04:38 +01:00
if ( nb < 4 ) {
matrix_data [ 1 ] = 1 < < ( 3 - nb ) ;
2016-01-28 21:21:50 +01:00
} else {
2020-02-17 18:04:38 +01:00
matrix_data [ 0 ] = 1 < < ( 35 - nb ) ;
2016-01-28 21:21:50 +01:00
}
2019-03-26 19:27:40 +01:00
2020-02-17 18:04:38 +01:00
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 ;
2016-01-28 21:21:50 +01:00
}
2020-02-17 18:04:38 +01:00
} 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 ;
2016-01-28 21:21:50 +01:00
}
}
matrix_data [ 1 ] & = ~ digit_mask ; // be sure only the bits for the matrix are used
2020-02-17 18:04:38 +01:00
matrix_data [ 1 ] | = ( driver_data [ nb ] [ 2 ] + ( driver_data [ nb ] [ 3 ] < < 16 ) ) & digit_mask ; // get the existing data for the digit
2016-01-28 21:21:50 +01:00
// prepare the data for SPI to shift it out
2020-02-17 18:04:38 +01:00
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 ;
2016-01-28 21:21:50 +01:00
}
}
void vfd_clear ( void )
{
2020-02-17 18:04:38 +01:00
for ( uint8_t i = 0 ; i < LENGTH ( driver_data ) ; i + + ) {
for ( uint8_t j = 0 ; j < LENGTH ( driver_data [ 0 ] ) ; j + + ) {
2016-01-28 21:21:50 +01:00
driver_data [ i ] [ j ] = 0 ;
}
}
}
void vfd_test ( void )
{
2020-02-17 18:04:38 +01:00
for ( uint8_t i = 0 ; i < LENGTH ( driver_data ) ; i + + ) {
for ( uint8_t j = 0 ; j < LENGTH ( driver_data [ 0 ] ) ; j + + ) {
2016-01-28 21:21:50 +01:00
driver_data [ i ] [ j ] = ~ 0 ;
}
}
}
void vfd_on ( void )
{
2020-02-17 18:04:38 +01:00
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
2016-01-28 21:21:50 +01:00
}
void vfd_off ( void )
{
2020-02-17 18:04:38 +01:00
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
2016-01-28 21:21:50 +01:00
}
void vfd_setup ( void )
{
/* setup GPIO to control the VFD */
2020-02-17 18:04:38 +01:00
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
2019-03-26 19:27:40 +01:00
2016-01-28 21:21:50 +01:00
/* setup SPI to transmit data */
2020-02-17 18:04:38 +01:00
rcc_periph_clock_enable ( RCC_SPI ( VFD_SPI ) ) ; // enable SPI clock
2020-03-23 11:41:08 +01:00
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
2020-02-17 18:04:38 +01:00
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
2016-01-28 21:21:50 +01:00
2020-02-17 18:04:38 +01:00
spi_reset ( SPI ( VFD_SPI ) ) ; // clear SPI values
2016-01-28 21:21:50 +01:00
/* set SPI:
* - use VFD_SPI port
* - divide clock by 8 for generating the baudrate ( F_PCLK1 is 36 MHz , max HV518 is 6 MHz )
* - 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 )
*/
2020-02-17 18:04:38 +01:00
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 ) ;
2016-01-28 21:21:50 +01:00
//spi_set_bidirectional_transmit_only_mode(VFD_SPI); // only use MOSI to transmit
2020-02-17 18:04:38 +01:00
spi_set_unidirectional_mode ( SPI ( VFD_SPI ) ) ; // MISO is unused
2016-01-28 21:21:50 +01:00
/* 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 */
2020-02-17 18:04:38 +01:00
spi_enable_software_slave_management ( SPI ( VFD_SPI ) ) ;
spi_set_nss_high ( SPI ( VFD_SPI ) ) ; // set NSS high
2019-03-26 19:27:40 +01:00
2020-02-17 18:04:38 +01:00
nvic_enable_irq ( SPI_IRQ ( VFD_SPI ) ) ; // enable SPI interrupt
spi_enable ( SPI ( VFD_SPI ) ) ; // enable SPI (the tx empty interrupt will trigger)
2019-03-26 19:27:40 +01:00
2016-01-28 21:21:50 +01:00
/* setup timer to refresh display */
2020-02-17 18:04:38 +01:00
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
2019-03-26 19:27:40 +01:00
2016-01-28 21:21:50 +01:00
vfd_clear ( ) ; // initialize values
}
2016-08-14 20:18:10 +02:00
/** SPI interrupt service routine called when data has been transmitted */
2020-02-17 18:04:38 +01:00
void SPI_ISR ( VFD_SPI ) ( void )
2016-01-28 21:21:50 +01:00
{
2020-02-17 18:04:38 +01:00
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
2016-01-28 21:21:50 +01:00
} else { // all data transmitted
2020-02-17 18:04:38 +01:00
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
2016-01-28 21:21:50 +01:00
}
}
}
2016-08-14 20:18:10 +02:00
/** timer interrupt service routine called time passed */
2020-02-17 18:04:38 +01:00
void TIM_ISR ( VFD_TIMER ) ( void )
2016-01-28 21:21:50 +01:00
{
2020-02-17 18:04:38 +01:00
if ( timer_get_flag ( TIM ( VFD_TIMER ) , TIM_SR_UIF ) ) { // overflow even happened
timer_clear_flag ( TIM ( VFD_TIMER ) , TIM_SR_UIF ) ; // clear flag
2020-03-23 11:41:08 +01:00
spi_i = 0 ; // set the register to shift out
2020-02-17 18:04:38 +01:00
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
2016-01-28 21:21:50 +01:00
}
}