2016-01-21 11:40:19 +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/>.
*
*/
/* Copyright (c) 2016 King Kévin <kingkevin@cuvoodoo.info> */
/* this library is used to drive the vacuum fluorescent display extracted from a Samsung SER-6500 cashier machine
* it used three chained supertex HV518P shift register VFD drivers */
/* 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
2016-01-22 16:22:47 +01:00
# include <libopencm3/stm32/spi.h> // SPI library
2016-01-24 17:26:53 +01:00
# include <libopencm3/stm32/timer.h> // timer library
2016-01-22 16:22:47 +01:00
# include <libopencm3/cm3/nvic.h> // interrupt handler
2016-01-21 11:40:19 +01:00
2016-01-24 17:26:53 +01:00
# include "global.h" // global definitions
# include "vfd.h" // VFD library API
2016-01-21 11:40:19 +01:00
2016-01-24 16:50:48 +01:00
/* supertex HV518 VFD driver pins */
/* port on which the pins to control the supertex HV518 VFD driver are
* we use port A because of the SPI interface */
# define VFD_PORT GPIOA
# define VFD_PORT_RCC RCC_GPIOA
/* SPI port to use */
# define VFD_SPI SPI1
# if (VFD_SPI==SPI1)
# define VFD_SPI_RCC RCC_SPI1
# define VFD_SPI_IRQ NVIC_SPI1_IRQ
# elif (VFD_SPI==SPI2)
# define VFD_SPI_RCC RCC_SPI2
# define VFD_SPI_IRQ NVIC_SPI2_IRQ
# endif
/* strobe pin to enable high voltage output
* high voltage is output on low
* drive using a GPIO PA6 ( normally MISO ) */
# define VFD_STR GPIO6
/* latch enable pin
* store the shifted data on low
* output the parallel data on high
* use GPIO ( PA4 ) ( NSS does not work as SS ) */
# define VFD_NLE GPIO4
/* clock signal
* drive using SPI SCK ( PA5 ) */
# define VFD_CLK GPIO_SPI1_SCK
/* data input, where the data is shifted to
* drive using SPI MOSI ( PA7 ) */
# define VFD_DIN GPIO_SPI1_MOSI
2016-01-24 17:26:53 +01:00
/* timer for automatic refresh */
# define VFD_TIMER TIM2
# if (VFD_TIMER==TIM2)
# define VFD_TIMER_RCC RCC_TIM2
# define VFD_TIMER_IRQ NVIC_TIM2_IRQ
# elif (VFD_TIMER==TIM3)
# define VFD_TIMER_RCC RCC_TIM3
# define VFD_TIMER_IRQ NVIC_TIM3_IRQ
# elif (VFD_TIMER==TIM4)
# define VFD_TIMER_RCC RCC_TIM4
# define VFD_TIMER_IRQ NVIC_TIM4_IRQ
# elif (VFD_TIMER==TIM5)
# define VFD_TIMER_RCC RCC_TIM5
# define VFD_TIMER_IRQ NVIC_TIM5_IRQ
# endif
2016-01-24 16:50:48 +01:00
2016-01-21 11:40:19 +01:00
/* ASCII characters encoded for 7 segments display
* starts with space
*/
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 , // ~
} ;
/* font for the 5x7 dot matrix display
* from http : //sunge.awardspace.com/glcd-sd/node4.html
* first value is left - most line
* LSB is top dot , MSB is not used
*/
2016-01-21 13:06:51 +01:00
static const uint8_t font5x7 [ ] [ 5 ] = {
2016-01-21 11:40:19 +01:00
{ 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 } // ~
} ;
/* pictures for the 5x7 dot matrix display
* first value is left - most line
* LSB is top dot , MSB is not used
*/
2016-01-21 13:06:51 +01:00
static const uint8_t pict5x7 [ ] [ 5 ] = {
2016-01-21 11:40:19 +01:00
{ 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-01-22 16:22:47 +01:00
/* the 32 bits values to be shifted out to the VFD driver
2016-01-25 00:39:29 +01:00
* split into 16 bit for SPI transfer
2016-01-25 10:15:09 +01:00
* since the bits for digits and matrix are independent , they can be combined
2016-01-25 00:39:29 +01:00
* we have more matrix ( 12 ) than digits ( 10 )
*/
static uint16_t driver_data [ VFD_MATRIX ] [ VFD_DRIVERS * 2 ] = { 0 } ;
static volatile uint8_t spi_i = 0 ; // which driver data is being transmitted
2016-01-25 10:15:09 +01:00
static volatile uint8_t vfd_grid = 0 ; // which grid/part to activate (single digits and matrix can be combined)
static const uint32_t digit_mask = 0x00fffff0 ; // the bits used for selecting then digit and 7 segment anodes (for the second driver)
2016-01-21 11:40:19 +01:00
/* set digit <nb> to ASCII character <c>
* use the MSB of < c > to enable the dot */
void vfd_digit ( uint8_t nb , char c )
{
2016-01-21 13:06:51 +01:00
if ( ! ( nb < VFD_DIGITS ) ) { // check the digit exists
2016-01-21 11:40:19 +01:00
return ;
}
2016-01-24 21:54:42 +01:00
2016-01-25 00:39:29 +01:00
uint32_t digit_data = 0 ; // the data to be shifted out for the driver (for the second driver)
2016-01-22 16:22:47 +01:00
2016-01-25 00:39:29 +01:00
digit_data = 1 < < ( 4 + ( 9 - nb ) ) ; // select digit
2016-01-21 11:40:19 +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)
2016-01-25 00:39:29 +01:00
digit_data | = ( 1 < < ( 14 ) ) ;
2016-01-21 11:40:19 +01:00
}
if ( c & 0x80 ) { // add the dot (encoded in the 8th bit)
2016-01-25 00:39:29 +01:00
digit_data | = ( 1 < < ( 15 ) ) ;
2016-01-21 11:40:19 +01:00
}
if ( false ) { // add the comma (not encoded)
2016-01-25 00:39:29 +01:00
digit_data | = ( 1 < < ( 16 ) ) ;
2016-01-21 11:40:19 +01:00
}
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 ) ) {
2016-01-25 00:39:29 +01:00
digit_data | = ( ascii_7segments [ i ] < < ( 17 ) ) ; // add encoded segments to memory
2016-01-21 11:40:19 +01:00
}
}
2016-01-24 21:54:42 +01:00
2016-01-25 00:39:29 +01:00
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)
2016-01-21 11:40:19 +01:00
}
/* set dot matrix <nb> to ASCII character <c>
* non ASCII characters are used for pictures */
void vfd_matrix ( uint8_t nb , char c )
{
2016-01-22 16:22:47 +01:00
// check the matrix exists
if ( ! ( nb < VFD_MATRIX ) ) {
2016-01-21 11:40:19 +01:00
return ;
2016-01-22 16:22:47 +01:00
}
2016-01-25 00:39:29 +01:00
uint32_t matrix_data [ VFD_DRIVERS ] = { 0 } ; // the data to be shifted out for the driver
2016-01-22 16:22:47 +01:00
// select matrix
if ( nb < 4 ) {
2016-01-25 00:39:29 +01:00
matrix_data [ 1 ] = 1 < < ( 3 - nb ) ;
2016-01-21 11:40:19 +01:00
} else {
2016-01-25 00:39:29 +01:00
matrix_data [ 0 ] = 1 < < ( 35 - nb ) ;
2016-01-21 11:40:19 +01:00
}
if ( ( c < 0x80 ) & & ( c > = ' ' ) ) { // only take printable characters
uint8_t i = c - ' ' ; // get index for character
2016-01-21 13:06:51 +01:00
if ( i < LENGTH ( font5x7 ) ) {
2016-01-25 00:39:29 +01:00
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-21 11:40:19 +01:00
}
} else if ( c > 0x7f ) { // the non ASCII character are used for pictures
uint8_t i = c - 0x80 ; // get index for character
2016-01-21 13:06:51 +01:00
if ( i < LENGTH ( pict5x7 ) ) {
2016-01-25 00:39:29 +01:00
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-21 11:40:19 +01:00
}
}
2016-01-25 00:39:29 +01:00
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
2016-01-24 21:54:42 +01:00
// prepare the data for SPI to shift it out
2016-01-25 00:39:29 +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-21 13:06:51 +01:00
}
2016-01-21 11:40:19 +01:00
}
2016-01-25 00:46:09 +01:00
/* clear VFD display */
2016-01-21 11:52:59 +01:00
void vfd_clear ( void )
{
2016-01-25 00:39:29 +01:00
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 ;
2016-01-24 21:54:42 +01:00
}
2016-01-21 11:52:59 +01:00
}
}
2016-01-25 00:46:09 +01:00
/* test VFD display (light up all anodes) */
2016-01-21 11:52:59 +01:00
void vfd_test ( void )
{
2016-01-25 00:39:29 +01:00
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 ;
2016-01-24 21:54:42 +01:00
}
2016-01-21 11:52:59 +01:00
}
}
2016-01-22 16:22:47 +01:00
/* switch VFD display on */
void vfd_on ( void )
{
gpio_clear ( VFD_PORT , VFD_STR ) ; // enable HV output
2016-01-25 00:46:09 +01:00
timer_enable_counter ( VFD_TIMER ) ; // start timer to periodically output that to the parts
2016-01-22 16:22:47 +01:00
}
/* switch VFD display off */
void vfd_off ( void )
{
gpio_set ( VFD_PORT , VFD_STR ) ; // disable HV output
2016-01-24 20:33:11 +01:00
timer_disable_counter ( VFD_TIMER ) ; // stop timer to periodically output that to the parts
}
2016-01-21 11:40:19 +01:00
/* setup VFD */
void vfd_setup ( void )
{
2016-01-24 17:26:53 +01:00
/* setup GPIO to control the VFD */
2016-01-22 16:22:47 +01:00
rcc_periph_clock_enable ( VFD_PORT_RCC ) ; // enable clock for VFD GPIO
2016-01-22 16:33:03 +01:00
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
2016-01-21 13:06:51 +01:00
2016-01-21 11:52:59 +01:00
gpio_set ( VFD_PORT , VFD_STR ) ; // disable HV output
2016-01-22 16:22:47 +01:00
gpio_clear ( VFD_PORT , VFD_NLE ) ; // do not output latched data
2016-01-24 17:26:53 +01:00
/* setup SPI to transmit data */
2016-01-22 16:22:47 +01:00
rcc_periph_clock_enable ( VFD_SPI_RCC ) ; // enable SPI clock
gpio_set_mode ( VFD_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_PORT , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , VFD_DIN ) ; // set VFD pin to alternative function push-pull
2016-01-24 17:26:53 +01:00
2016-01-22 16:22:47 +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 )
*/
2016-01-22 16:33:03 +01:00
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 ) ;
2016-01-22 16:22:47 +01:00
//spi_set_bidirectional_transmit_only_mode(VFD_SPI); // only use MOSI to transmit
2016-01-22 16:33:03 +01:00
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 */
2016-01-22 16:22:47 +01:00
spi_enable_software_slave_management ( VFD_SPI ) ;
spi_set_nss_high ( VFD_SPI ) ; // set NSS high
2016-01-22 16:33:03 +01:00
2016-01-22 16:22:47 +01:00
nvic_enable_irq ( VFD_SPI_IRQ ) ; // enable SPI interrupt
2016-01-24 21:54:42 +01:00
spi_enable ( VFD_SPI ) ; // enable SPI (the tx empty interrupt will trigger)
2016-01-24 17:26:53 +01:00
/* 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
2016-01-25 10:15:09 +01:00
timer_set_period ( VFD_TIMER , 0xffff / LENGTH ( driver_data ) / 100 ) ; // set the refresh frequency
2016-01-24 17:26:53 +01:00
timer_enable_irq ( VFD_TIMER , TIM_DIER_UIE ) ; // enable interrupt for timer
nvic_enable_irq ( VFD_TIMER_IRQ ) ; // allow interrupt for timer
2016-01-22 16:22:47 +01:00
vfd_clear ( ) ; // initialize values
}
# if (VFD_SPI==SPI1)
void spi1_isr ( void )
# elif (VFD_SPI==SPI2)
void spi2_isr ( void )
# endif
{
if ( SPI_SR ( VFD_SPI ) & SPI_SR_TXE ) { // transmission buffer empty
2016-01-25 00:39:29 +01:00
if ( spi_i < LENGTH ( driver_data [ 0 ] ) ) { // check if data is available
2016-01-22 16:22:47 +01:00
gpio_clear ( VFD_PORT , VFD_NLE ) ; // slave select to latch data
2016-01-25 10:15:09 +01:00
spi_send ( VFD_SPI , driver_data [ vfd_grid ] [ spi_i + + ] ) ; // send next data
2016-01-22 16:22:47 +01:00
} else { // all data transmitted
2016-01-24 21:54:42 +01:00
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-01-22 16:22:47 +01:00
}
}
2016-01-21 11:40:19 +01:00
}
2016-01-24 17:26:53 +01:00
# if (VFD_TIMER==TIM2)
void tim2_isr ( void )
# elif (VFD_TIMER==TIM3)
void tim3_isr ( void )
# elif (VFD_TIMER==TIM4)
void tim4_isr ( void )
# elif (VFD_TIMER==TIM5)
void tim5_isr ( void )
# endif
{
if ( timer_get_flag ( VFD_TIMER , TIM_SR_UIF ) ) { // overflow even happened
timer_clear_flag ( VFD_TIMER , TIM_SR_UIF ) ; // clear flag
2016-01-25 00:39:29 +01:00
spi_i = 0 ; // set the register to shift out
2016-01-24 21:54:42 +01:00
spi_enable_tx_buffer_empty_interrupt ( VFD_SPI ) ; // enable TX empty interrupt
2016-01-25 10:15:09 +01:00
vfd_grid = ( vfd_grid + 1 ) % LENGTH ( driver_data ) ; // got to next segment
2016-01-24 17:26:53 +01:00
}
}