Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
PT6301.cpp
- Committer:
- wim
- Date:
- 2021-06-13
- Revision:
- 1:aa0195b0f83c
- Parent:
- PT6302.cpp@ 0:ecc29c13a997
File content as of revision 1:aa0195b0f83c:
/* mbed PT6301 Library, for Princeton LC7571X VFD controller
* The controller is used by Futaba 'Chip In Glass' (CIG) VFD tubes.
*
* Copyright (c) 2021, v01: WH, Initial version
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "mbed.h"
#include "PT6301.h"
#include "PT6301_UDC.inc"
/** Constructor for class for driving Princeton PT6301 VFD controller
*
* @brief Supports upto 20 Grids of 5x7 matrix segments for 2 rows of characters (row A and B).
* also supports 2 additional segments for 2 rows of characters (row A and B).
* SPI bus interface device.
* @param PinName mosi, sclk, cs SPI bus pins
* @param Mode selects number of Grids and Rows (default 20 Grids, 2 rows)
* @param bool inverted_rows selects mapping of Data onto Display layout (default false)
* @param Columns selects number of characters per row (default 20, same as Mode Grids)
* @param Rows selects number of rows (default 2, same as Mode Rows)
*/
PT6301::PT6301(PinName mosi, PinName sclk, PinName cs, PinName rst, Mode mode, bool inverted_rows, int columns, int rows) : _spi(mosi,NC,sclk), _cs(cs), _rst(rst), _mode(mode), _inverted_rows(inverted_rows), _columns(columns), _rows(rows) {
_init();
}
/** Init the PT6301 interface and the controller
*
* @param none
* @return none
*/
void PT6301::_init(){
//init SPI
_cs=1;
_spi.format(8,3); //PT6301 uses mode 3 (Clock High on Idle, Data latched on second (=rising) edge)
_spi.frequency(100000);
// _spi.frequency(250000);
//init controller
#if(0)
// Reset (3V3 level too low? Add pull-up to 5V)
_rst=1;
wait_ms(PT6301_RST_DLY);
_rst=0;
wait_ms(PT6301_RST_DLY);
_rst=1;
wait_ms(PT6301_RST_DLY);
#endif
// Set number of Grids
_writeCmd((PT6301_GRID_REG | (_mode & PT6301_GRID_MSK))); // Command register & value
setBrightness(PT6301_BRT_DEF); // Default Brightness
// Clear the DCRAM and ADRAM (undefined at Reset) and reset (_row, _column)
cls(true);
// Clear the UDC RAM (undefined at Reset)
const char udc_none[] = {0x00,0x00,0x00,0x00,0x00};
for (int idx=0; idx < PT6301_NR_UDC; idx++) {
setUDC(idx, (char *)udc_none);
}
// Update the display
refresh();
setDisplay(true); // Display On
}
/** Clear the screen and locate to (0,0)
*
* @param bool clrAll Clear Icons also (default = false)
* @return none
*/
void PT6301::cls(bool clrAll) {
for (_row = 0; _row < _rows; _row++) {
for (_column = 0; _column < _columns; _column++) {
_displaybuffer[_row][_column] = ' '; // data
if (clrAll) {
_addbuffer[_row][_column] = 0; // icons
}
}
}
_row = 0;
_column = 0;
}
/** Locate cursor to a screen row, column
*
* @param row The vertical position from the top, indexed from 0
* @param column The horizontal position from the left, indexed from 0
* @return none
*/
void PT6301::locate(int row, int column) {
//sanity check
if (row < 0) {row = 0;}
if (row > (_rows - 1)) {row = _rows - 1;}
if (column < 0) {column = 0;}
if (column > (_columns - 1)) {column = _columns - 1;}
_row = row;
_column = column;
}
/** Number of screen columns
*
* @param none
* @return columns
*/
int PT6301::columns(){
return _columns;
}
/** Number of screen rows
*
* @param none
* @return rows
*/
int PT6301::rows() {
return _rows;
}
/** Refresh screen and show data in local mirrors on the display
*
* @param bool copyAll Copy Icons in Adat local mirror also (default = true)
* @return none
*/
void PT6301::refresh(bool copyAll) {
int row_cnt, col_cnt;
//Copy character data mirror to display
_cs=0; // Send Command for DATA_A_REG
wait_us(1);
_spi.write(_flip(PT6301_DATA_A_REG)); // Command register for DATA_A
wait_us(PT6301_CMD_DLY); // Command Delay
row_cnt = _row_flip(0); // Reorder rows depending on VFD layout
for (col_cnt = 0; col_cnt < _columns; col_cnt++) {
_spi.write(_flip(_displaybuffer[row_cnt][col_cnt])); // DATA_A Row
}
wait_us(PT6301_CS_DLY); // CS Hold Delay
_cs=1; // Latch Command & Params
wait_us(PT6301_CMD_DLY); // Command Delay
_cs=0; // Send Command for DATA_B_REG
wait_us(1);
_spi.write(_flip(PT6301_DATA_B_REG)); // Command register for DATA_B
wait_us(PT6301_CMD_DLY); // Command Delay
row_cnt = _row_flip(1); // Reorder rows depending on VFD layout
for (col_cnt = 0; col_cnt < _columns; col_cnt++) {
_spi.write(_flip(_displaybuffer[row_cnt][col_cnt])); // DATA_B Row
}
wait_us(PT6301_CS_DLY); // CS Hold Delay
_cs=1; // Latch Command & Params
//Copy icon data mirror to display
if (copyAll) {
_cs=0; // Send Command for ADAT_A_REG
wait_us(1);
_spi.write(_flip(PT6301_ADAT_A_REG)); // Command register for ADAT_A
wait_us(PT6301_CMD_DLY); // Command Delay
row_cnt = _row_flip(0); // Reorder rows depending on VFD layout
for (col_cnt = 0; col_cnt < _columns; col_cnt++) {
_spi.write(_flip(_addbuffer[row_cnt][col_cnt])); // ADAT_A Row
}
wait_us(PT6301_CS_DLY); // CS Hold Delay
_cs=1; // Latch Command & Params
wait_us(PT6301_CMD_DLY); // Command Delay
_cs=0; // Send Command for ADAT_B_REG
wait_us(1);
_spi.write(_flip(PT6301_ADAT_B_REG)); // Command register for ADAT_B
wait_us(PT6301_CMD_DLY); // Command Delay
row_cnt = _row_flip(1); // Reorder rows depending on VFD layout
for (col_cnt = 0; col_cnt < _columns; col_cnt++) {
_spi.write(_flip(_addbuffer[row_cnt][col_cnt])); // ADAT_B Row
}
wait_us(PT6301_CS_DLY); // CS Hold Delay
_cs=1; // Latch Command & Params
}
}
/** Set Brightness
*
* @param char brightness (valid range 0..255)
* @return none
*/
void PT6301::setBrightness(char brightness){
//Sanity check
//
_writeCmd(PT6301_BRT_REG, brightness); // Command register & value
}
/** Set the Display mode On/off
*
* @param bool display mode
* @return none
*/
void PT6301::setDisplay(bool on) {
char display;
if (on) {
display = PT6301_DSPL_NRM; // normal mode, show Display RAM content
}
else {
display = PT6301_DSPL_OFF; // all segments off
}
_writeCmd((PT6301_DSPL_REG | display)); // Command register & value
}
/** Set the Display test mode On/off
*
* @param bool display test mode
* @return none
*/
void PT6301::setDisplayTest(bool on) {
char display;
if (on) {
display = PT6301_DSPL_ON; // test mode, all segments on
}
else {
display = PT6301_DSPL_NRM; // normal mode, show Display RAM content
}
_writeCmd((PT6301_DSPL_REG | display)); // Command register & value
}
/** Set User Defined Characters (UDC) for A and B
*
* @param unsigned char udc_idx The Index of the UDC (0..15)
* @param UDCData_t udc_data The bitpattern for the UDC (5 bytes)
* @return none
*/
void PT6301::setUDC(unsigned char udc_idx, UDCData_t udc_data) {
//Sanity check
udc_idx = udc_idx & PT6301_UADR_MSK; // mask invalid bits
_cs=0; // Send Command & Params for UDC_A
wait_us(1);
_spi.write(_flip(PT6301_UDC_A_REG | udc_idx)); // Command register & address for UDC_A
wait_us(PT6301_CMD_DLY); // Command Delay
_spi.write(_flip(udc_data[0] & PT6301_UDC_MSK)); // CD30 CD25 ...... CD0
_spi.write(_flip(udc_data[1] & PT6301_UDC_MSK)); // CD31 CD26 ...... CD1
_spi.write(_flip(udc_data[2] & PT6301_UDC_MSK)); // CD32 CD27 ...... CD2
_spi.write(_flip(udc_data[3] & PT6301_UDC_MSK)); // CD33 CD28 ...... CD3
_spi.write(_flip(udc_data[4] & PT6301_UDC_MSK)); // CD34 CD29 ...... CD4
wait_us(PT6301_CS_DLY); // CS Hold Delay
_cs=1; // Latch Command & Params
wait_us(PT6301_CMD_DLY); // Command Delay
_cs=0; // Send Command & Params for UDC B
wait_us(1);
_spi.write(_flip(PT6301_UDC_B_REG | udc_idx)); // Command register & address for UDC_B
wait_us(PT6301_CMD_DLY); // Command Delay
_spi.write(_flip(udc_data[0] & PT6301_UDC_MSK)); // CD30 CD25 ...... CD0
_spi.write(_flip(udc_data[1] & PT6301_UDC_MSK)); // CD31 CD26 ...... CD1
_spi.write(_flip(udc_data[2] & PT6301_UDC_MSK)); // CD32 CD27 ...... CD2
_spi.write(_flip(udc_data[3] & PT6301_UDC_MSK)); // CD33 CD28 ...... CD3
_spi.write(_flip(udc_data[4] & PT6301_UDC_MSK)); // CD34 CD29 ...... CD4
wait_us(PT6301_CS_DLY); // CS Hold Delay
_cs=1; // Latch Command & Params
wait_us(PT6301_CMD_DLY); // Command Delay
}
/** Set Icon
*
* @param int row The row of the icon (0..(rows-1))
* @param int column The column of the icon (0..(cols-1))
* @return none
*/
void PT6301::setIcon(int row, int column){
//sanity check
if (row < 0) {row = 0;}
if (row > (_rows - 1)) {row = _rows - 1;}
if (column < 0) {column = 0;}
if (column > (_columns - 1)) {column = _columns - 1;}
_addbuffer[row][column] = PT6301_ADAT_MSK;
}
/** Clr Icon
*
* @param int row The row of the icon (0..(rows-1))
* @param int column The column of the icon (0..(cols-1))
* @return none
*/
void PT6301::clrIcon(int row, int column){
//sanity check
if (row < 0) {row = 0;}
if (row > (_rows - 1)) {row = _rows - 1;}
if (column < 0) {column = 0;}
if (column > (_columns - 1)) {column = _columns - 1;}
_addbuffer[row][column] = 0x00;
}
/** Write command to PT6301
*
* @param char cmd Command byte
* @return none
*/
void PT6301::_writeCmd(char cmd){
_cs=0; // Prepare to send Command
wait_us(1);
_spi.write(_flip(cmd)); // Command register & value
wait_us(PT6301_CS_DLY); // CS Hold Delay
_cs=1; // Latch Command
wait_us(PT6301_CMD_DLY); // Command Delay
}
/** Write command and data to PT6301
*
* @param char cmd Command byte
* @param char data Parameter for command
* @return none
*/
void PT6301::_writeCmd(char cmd, char data){
_cs=0; // Prepare to send Command and data
wait_us(1);
_spi.write(_flip(cmd)); // Command register & value
wait_us(PT6301_CMD_DLY); // Command Delay
_spi.write(_flip(data)); // data
wait_us(PT6301_CS_DLY); // CS Hold Delay
_cs=1; // Latch Command and data
wait_us(PT6301_CMD_DLY); // Command Delay
}
/** Write Data to local mirror
*
* @param char data The databyte
* @param row The vertical position from the top, indexed from 0
* @param column The horizontal position from the left, indexed from 0
* @return none
*/
void PT6301::setData(char data, int row, int column){
//Sanity check, allow access to all of local mirror
if (row < 0) {row = 0;}
if (row > (PT6301_MAX_NR_ROWS - 1)) {row = PT6301_MAX_NR_ROWS - 1;}
if (column < 0) {column = 0;}
if (column > (PT6301_MAX_NR_GRIDS - 1)) {column = PT6301_MAX_NR_GRIDS - 1;}
_displaybuffer[row][column] = data;
}
/** Read Data from local mirror
*
* @param row The vertical position from the top, indexed from 0
* @param column The horizontal position from the left, indexed from 0
* @return char The databyte
*/
char PT6301::getData(int row, int column){
//Sanity check, allow access to all of local mirror
if (row < 0) {row = 0;}
if (row > (PT6301_MAX_NR_ROWS - 1)) {row = PT6301_MAX_NR_ROWS - 1;}
if (column < 0) {column = 0;}
if (column > (PT6301_MAX_NR_GRIDS - 1)) {column = PT6301_MAX_NR_GRIDS - 1;}
return _displaybuffer[row][column];
}
/** Write AData to local mirror
*
* @param char data The symbol databyte
* @param row The vertical position from the top, indexed from 0
* @param column The horizontal position from the left, indexed from 0
* @return none
*/
void PT6301::setAData(char data, int row, int column){
//Sanity check, allow access to all of local mirror
if (row < 0) {row = 0;}
if (row > (PT6301_MAX_NR_ROWS - 1)) {row = PT6301_MAX_NR_ROWS - 1;}
if (column < 0) {column = 0;}
if (column > (PT6301_MAX_NR_GRIDS - 1)) {column = PT6301_MAX_NR_GRIDS - 1;}
_addbuffer[row][column] = data & PT6301_ADAT_MSK;
}
/** Read AData from local mirror
*
* @param row The vertical position from the top, indexed from 0
* @param column The horizontal position from the left, indexed from 0
* @return char The symbol databyte
*/
char PT6301::getAData(int row, int column){
//Sanity check, allow access to all of local mirror
if (row < 0) {row = 0;}
if (row > (PT6301_MAX_NR_ROWS - 1)) {row = PT6301_MAX_NR_ROWS - 1;}
if (column < 0) {column = 0;}
if (column > (PT6301_MAX_NR_GRIDS - 1)) {column = PT6301_MAX_NR_GRIDS - 1;}
return _addbuffer[row][column];
}
/** Helper to reverse all command or databits. The PT6301 expects LSB first, whereas SPI is MSB first
*
* @param char data
* @return bitreversed data
*/
char PT6301::_flip(char data) {
char value=0;
if (data & 0x01) {value |= 0x80;} ;
if (data & 0x02) {value |= 0x40;} ;
if (data & 0x04) {value |= 0x20;} ;
if (data & 0x08) {value |= 0x10;} ;
if (data & 0x10) {value |= 0x08;} ;
if (data & 0x20) {value |= 0x04;} ;
if (data & 0x40) {value |= 0x02;} ;
if (data & 0x80) {value |= 0x01;} ;
return value;
}
/** Helper to reverse row idx depending on VFD layout
*
* @param int row_idx
* @return adjusted row_idx
*/
int PT6301::_row_flip(int row_idx) {
if (_inverted_rows) {
return (1 - row_idx); // Reorder row mapping to match VFD layout
// Top line is DATA_B_REG, ADAT_B_REG
// Bottom line is DATA_A_REG, ADAT_A_REG
}
else {
return row_idx; // Maintain row mapping to match VFD layout
// Top line is DATA_A_REG, ADAT_A_REG
// Bottom line is DATA_B_REG, ADAT_B_REG
}
}
/** Write a single character (Stream implementation)
*
* @param value char to print
* @return value;
*/
int PT6301::_putc(int value) {
if (value == '\r') {
//No character to write
//Update Cursor
_column = 0;
}
else if (value == '\n') {
//No character to write
//Update Cursor
_row++;
if (_row > (_rows - 1)) {
_row = 0;
}
}
else if ((value >= 0) && (value < 256)) {
//Valid character to write
//Write displaybuffer entry
_displaybuffer[_row][_column] = value;
//Update Cursor
_column++;
if (_column > (_columns - 1)) {
_column = 0;
_row++;
}
if (_row > (_rows - 1)) {
_row = 0;
}
} // if validChar
return value;
}
/** Get a single character (Stream implementation)
*
* @param none
* @return -1
*/
int PT6301::_getc() {
return -1;
}
#if (SMTG7400_TEST == 1)
/** Constructor for class for Princeton PT6301 VFD controller as used in SMTG7400
*
* @brief Supports 16 Grids of 5x7 Segments with 4 additional Segments in use.
*
* @param PinName mosi, miso, sclk, cs SPI bus pins
* @param PinName rst Reset pin
*/
PT6301_SMTG7400::PT6301_SMTG7400(PinName mosi, PinName sclk, PinName cs, PinName rst) : PT6301(mosi, sclk, cs, rst, Grid16, true, SMTG7400_NR_COLS, SMTG7400_NR_ROWS) {
}
/** Set Icon
*
* @param int icon The icon ID
* @return none
*/
void PT6301_SMTG7400::setIcon(int icon) {
PT6301::setIcon((icon >> SMTG7400_ICON_ROW_SHFT), (icon & SMTG7400_ICON_COL_MSK));
}
/** Clr Icon
*
* @param int icon The icon ID
* @return none
*/
void PT6301_SMTG7400::clrIcon(int icon) {
PT6301::clrIcon((icon >> SMTG7400_ICON_ROW_SHFT), (icon & SMTG7400_ICON_COL_MSK));
}
#endif
#if (SMTC7140_TEST == 1)
/** Constructor for class for Princeton PT6301 VFD controller as used in SMTC7140
*
* @brief Supports 12 Grids of 5x7 Segments without additional Icon Segments, for 2 Rows.
* Grid13 is used for icons displayed by a UDC symbol.
*
* @param PinName mosi, miso, sclk, cs SPI bus pins
* @param PinName rst Reset pin
*/
PT6301_SMTC7140::PT6301_SMTC7140(PinName mosi, PinName sclk, PinName cs, PinName rst) : PT6301(mosi, sclk, cs, rst, Grid13, true, SMTC7140_NR_COLS, SMTC7140_NR_ROWS) {
//Enable VGen for VFD Power Supply
//Note this is wrong because we should send the init commands to the PT6301 before the 5V powersupply is enabled !
// setVGen(true);
}
/** Set VFD VGen
*
* @param bool on
* @return none
*/
void PT6301_SMTC7140::setVGen (bool on) {
}
/** Set IconGrid13
* Icons are shown on Grid13 using the UDC at index=0. The UDC char=0 is stored in _displaybuffer[0][12] at reset.
* This method will set the correct segment in the UDC for each icon.
*
* @param int icon The icon ID
* @return none
*/
void PT6301_SMTC7140::setIconGrid13(int icon) {
#if(0)
//Test version to check all bits
// clear icon
for (int udc_col=0; udc_col<5; udc_col++) {
_icon_data[udc_col] = 0x00;
};
_icon_data[icon >> 8] = (icon & 0x7F);
setUDC(0, (char *) _icon_data); // Store mirror for UDC_idx=0
#else
//Normal version
for (int udc_col=0; udc_col<5; udc_col++) {
_icon_data[udc_col] = _icon_data[udc_col] | SMTC7140_ICONS[icon][udc_col]; // OR icon bitpattern with UDC mirror for UDC_idx=0
}
setUDC(0, (char *) _icon_data); // Store mirror for UDC_idx=0
#endif
}
/** Clr IconGrid13
* Icons are shown on Grid13 using the UDC at index=0. The UDC char=0 is stored in _displaybuffer[0][12] at reset.
* This method will clr the correct segment in the UDC for each icon.
*
* @param int icon The icon ID
* @return none
*/
void PT6301_SMTC7140::clrIconGrid13(int icon) {
for (int udc_col=0; udc_col<5; udc_col++) {
_icon_data[udc_col] = _icon_data[udc_col] & ~(SMTC7140_ICONS[icon][udc_col]); // AND inverted icon bitpattern with UDC mirror for UDC_idx=0
}
setUDC(0, (char *) _icon_data); // Store mirror for UDC_idx=0
}
#endif