Wim Huiskamp / PT6301

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