2016-01-28 21:21:50 +01:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
2016-08-14 20:18:10 +02:00
/** library to drive vacuum fluorescent display using supertex HV518 shift register VFD drivers (code)
* @ details the current configuration is for a VFD extracted from a Samsung SER - 6500 cash register
* @ file vfd_hv518 . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
* @ date 2016
* @ 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
* @ {
*/
# define VFD_PORT GPIOA /**< GPIO port */
# define VFD_PORT_RCC RCC_GPIOA /**< GPIO port peripheral clock */
# define VFD_STR GPIO6 /**< strobe pin to enable high voltage output, high voltage is output on low */
# define VFD_NLE GPIO4 /**< 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_RCC RCC_SPI1 /**< SPI peripheral */
# define VFD_SPI_PORT GPIOA /**< GPIO port */
# define VFD_SPI_PORT_RCC RCC_GPIOA /**< GPIO port peripheral clock */
# define VFD_SPI_IRQ NVIC_SPI1_IRQ /**< SPI peripheral interrupt signal */
# define VFD_SPI_ISR spi1_isr /**< SPI interrupt service routine */
# define VFD_CLK GPIO_SPI1_SCK /**< clock signal */
# define VFD_DIN GPIO_SPI1_MOSI /**< data input, where the data is shifted to */
/** @} */
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
* @ {
*/
# define VFD_TIMER_RCC RCC_TIM2 /**< timer peripheral clock */
# define VFD_TIMER_IRQ NVIC_TIM2_IRQ /**< timer interrupt signal */
# define VFD_TIMER_ISR tim2_isr /**< timer interrupt service routine */
/** @} */
/** 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 [ ] = {
0 b00000000 , // space
0 b00110000 , // ! (I)
0 b00100010 , // "
0 b01011100 , // # (o)
0 b01101101 , // $ (s)
0 b01010010 , // % (/)
0 b01111101 , // & (6)
0 b00100000 , // '
0 b00111001 , // ( ([)
0 b00001111 , // )
0 b01110000 , // *
0 b01000110 , // +
0 b00010000 , // ,
0 b01000000 , // -
0 b00010000 , // . (,)
0 b01010010 , // /
0 b00111111 , // 0
0 b00000110 , // 1
0 b01011011 , // 2
0 b01001111 , // 3
0 b01100110 , // 4
0 b01101101 , // 5
0 b01111101 , // 6
0 b00000111 , // 7
0 b01111111 , // 8
0 b01101111 , // 9
0 b01001000 , // : (=)
0 b01001000 , // ; (=)
0 b01011000 , // <
0 b01001000 , // =
0 b01001100 , // >
0 b01010011 , // ?
0 b01111011 , // @
0 b01110111 , // A
0 b01111111 , // B
0 b00111001 , // C
0 b01011110 , // D
0 b01111001 , // E
0 b01110001 , // F
0 b00111101 , // G
0 b01110110 , // H
0 b00110000 , // I
0 b00011110 , // J
0 b01110110 , // K
0 b00111000 , // L
0 b00110111 , // M
0 b00110111 , // N
0 b00111111 , // O
0 b01110011 , // P
0 b01101011 , // Q
0 b00110011 , // R
0 b01101101 , // S
0 b01111000 , // T
0 b00111110 , // U
0 b00111110 , // V (U)
0 b00111110 , // W (U)
0 b01110110 , // X (H)
0 b01101110 , // Y
0 b01011011 , // Z
0 b00111001 , // [
0 b01100100 , // '\'
0 b00001111 , // /
0 b00100011 , // ^
0 b00001000 , // _
0 b00000010 , // `
0 b01011111 , // a
0 b01111100 , // b
0 b01011000 , // c
0 b01011110 , // d
0 b01111011 , // e
0 b01110001 , // f
0 b01101111 , // g
0 b01110100 , // h
0 b00010000 , // i
0 b00001100 , // j
0 b01110110 , // k
0 b00110000 , // l
0 b01010100 , // m
0 b01010100 , // n
0 b01011100 , // o
0 b01110011 , // p
0 b01100111 , // q
0 b01010000 , // r
0 b01101101 , // s
0 b01111000 , // t
0 b00011100 , // u
0 b00011100 , // v (u)
0 b00011100 , // w (u)
0 b01110110 , // x
0 b01101110 , // y
0 b01011011 , // z
0 b00111001 , // { ([)
0 b00110000 , // |
0 b00001111 , // } ([)
0 b01000000 , // ~
} ;
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 } , // }
{ 0 b00001000 , 0 b00000100 , 0 b00001100 , 0 b00001000 , 0 b00000100 } // ~
} ;
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 } , // <-
{ 0 b01110000 , 0 b01110000 , 0 b01111010 , 0 b01111100 , 0 b01011000 } , // bunny side 1
{ 0 b00100000 , 0 b01110000 , 0 b01110010 , 0 b01111100 , 0 b01011000 } , // bunny side 2
{ 0 b00111110 , 0 b01001001 , 0 b01010110 , 0 b01001001 , 0 b00111110 } , // bunny face 1
{ 0 b00111110 , 0 b01010001 , 0 b01100110 , 0 b01010001 , 0 b00111110 } , // bunny face 2
{ 0 b00111000 , 0 b01010111 , 0 b01100100 , 0 b01010111 , 0 b00111000 } , // bunny face 3
{ 0 b00111000 , 0 b01001111 , 0 b01010100 , 0 b01001111 , 0 b00111000 } , // bunny face 4
{ 0 b00111000 , 0 b01011110 , 0 b01101000 , 0 b01011110 , 0 b00111000 } , // bunny face 5
{ 0 b01000001 , 0 b00110110 , 0 b00001000 , 0 b00110110 , 0 b01000001 } , // cross 1
{ ~ 0 b01000001 , ~ 0 b00110110 , ~ 0 b00001000 , ~ 0 b00110110 , ~ 0 b01000001 } , // cross 1 negated
{ 0 b00100010 , 0 b00010100 , 0 b00001000 , 0 b00010100 , 0 b00100010 } , // cross 2
{ ~ 0 b00100010 , ~ 0 b00010100 , ~ 0 b00001000 , ~ 0 b00010100 , ~ 0 b00100010 } , // cross 2 negated
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } // nothing
} ;
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
*/
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 ;
/** 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 ;
2016-01-28 21:21:50 +01:00
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 ( VFD_PORT , VFD_STR ) ; // enable HV output
timer_enable_counter ( VFD_TIMER ) ; // start timer to periodically output that to the parts
}
void vfd_off ( void )
{
gpio_set ( VFD_PORT , VFD_STR ) ; // disable HV output
timer_disable_counter ( 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 ( VFD_PORT_RCC ) ; // enable clock for VFD GPIO
gpio_set_mode ( VFD_PORT , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , VFD_STR ) ; // set VFD pin to output push-pull
gpio_set_mode ( VFD_PORT , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , VFD_NLE ) ; // set VFD pin to output push-pull
gpio_set ( VFD_PORT , VFD_STR ) ; // disable HV output
gpio_clear ( VFD_PORT , VFD_NLE ) ; // do not output latched data
/* setup SPI to transmit data */
rcc_periph_clock_enable ( VFD_SPI_RCC ) ; // enable SPI clock
2016-08-14 20:18:10 +02:00
rcc_periph_clock_enable ( VFD_SPI_PORT_RCC ) ; // enable clock for VFD SPI GPIO
gpio_set_mode ( VFD_SPI_PORT , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , VFD_CLK ) ; // set VFD pin to alternative function push-pull
gpio_set_mode ( VFD_SPI_PORT , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , VFD_DIN ) ; // set VFD pin to alternative function push-pull
2016-01-28 21:21:50 +01:00
spi_reset ( VFD_SPI ) ; // clear SPI values
/* 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 )
*/
spi_init_master ( 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 ( 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 ( VFD_SPI ) ;
spi_set_nss_high ( VFD_SPI ) ; // set NSS high
nvic_enable_irq ( VFD_SPI_IRQ ) ; // enable SPI interrupt
spi_enable ( VFD_SPI ) ; // enable SPI (the tx empty interrupt will trigger)
/* setup timer to refresh display */
rcc_periph_clock_enable ( VFD_TIMER_RCC ) ; // enable clock for timer block
timer_reset ( VFD_TIMER ) ; // reset timer state
timer_set_mode ( 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 ( VFD_TIMER , ( SYSTEM_CLOCK_FREQ / ( 1 < < 16 ) ) - 1 ) ; // set the prescaler so this 16 bits timer overflows at 1Hz
timer_set_period ( VFD_TIMER , 0xffff / LENGTH ( driver_data ) / 100 ) ; // set the refresh frequency
timer_enable_irq ( VFD_TIMER , TIM_DIER_UIE ) ; // enable interrupt for timer
nvic_enable_irq ( VFD_TIMER_IRQ ) ; // allow interrupt for timer
vfd_clear ( ) ; // initialize values
}
2016-08-14 20:18:10 +02:00
/** SPI interrupt service routine called when data has been transmitted */
void VFD_SPI_ISR ( void )
2016-01-28 21:21:50 +01:00
{
if ( SPI_SR ( VFD_SPI ) & SPI_SR_TXE ) { // transmission buffer empty
if ( spi_i < LENGTH ( driver_data [ 0 ] ) ) { // check if data is available
gpio_clear ( VFD_PORT , VFD_NLE ) ; // slave select to latch data
spi_send ( VFD_SPI , driver_data [ vfd_grid ] [ spi_i + + ] ) ; // send next data
} else { // all data transmitted
spi_disable_tx_buffer_empty_interrupt ( VFD_SPI ) ; // no need to wait for new data
while ( SPI_SR ( VFD_SPI ) & SPI_SR_BSY ) ; // wait for data to be shifted out
spi_disable_tx_buffer_empty_interrupt ( VFD_SPI ) ; // no need to wait for new data
gpio_set ( VFD_PORT , VFD_NLE ) ; // output latched data
}
}
}
2016-08-14 20:18:10 +02:00
/** timer interrupt service routine called time passed */
void VFD_TIMER_ISR ( void )
2016-01-28 21:21:50 +01:00
{
if ( timer_get_flag ( VFD_TIMER , TIM_SR_UIF ) ) { // overflow even happened
timer_clear_flag ( VFD_TIMER , TIM_SR_UIF ) ; // clear flag
spi_i = 0 ; // set the register to shift out
spi_enable_tx_buffer_empty_interrupt ( VFD_SPI ) ; // enable TX empty interrupt
vfd_grid = ( vfd_grid + 1 ) % LENGTH ( driver_data ) ; // got to next segment
}
}