// 7-Segment display driver using SPI
// XC3714 driver.ino
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include "XC3714_driver.h"
T_seven_seg_errors seven_seg_errors; // trapping and recording any seven segment display errors
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------
// When we drive the 7 segment display, our first job is to format a buffer of digits from the floating point or integer argument "num".
// When our lowset digit measures whole microns, there can't be more than 3 digits to the right of the decimal point (thousandths of a millimeter, single microns).
// There are up to four significant digits to the left of the decimal point. 9999.999 meters at a resolution of one micron. (or approx ten thousand millimeters)
// There will be a blank digit to the left of the significant digits that will optionally display a minus sign, for negative numbers.
// Using this method effectively gives 'leading-zero' suppression except where there is less than 1 mm displayed, as the value will be for example "-0.999" or "-0.001".
// The printf() formatter behaviour for %1.3f, cannot be displayed in 8 digits for all sizes beyond 4 LSD's.
// When the maximum number of significant digits has been exceeded, while counting from 9999 to 10000, we lose the least signifcant digit.
// then everything moves one step to the right including the decimal point.
// However, when the most significant digits reduce to four digits, we again see the 3 'least significant digits' (thousandths of a mm or 10E-6 metres).
// The maximum full number is +9999.999 or -9999.999 with a single lsb resolution. [one part in 10 million]
// In other words, we can dislay approx "plus or minus 10 million" as an integer.
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Testing the SPI clock rate: This has been run at 5.25 Mhz with a 16 bit frame width.
// SPI runs as 'Transmit Master Only'.
// Low level chip select (/CS) is connected to Port 10 via a 74HC14 inverter. Use macros "DISABLE_7SEG" and "ENABLE_7SEG"
// For a 3 axis set of measuremements, Teensy P2, P3, P4 will be used to select the correct 7-segment display.
// Level shifting from 3.3 V SPI outputs to 5.0V is done using 74HC14 schmitt trigger devices.
// We only use the absolute magnitude of the float "num", so we don't see the minus sign in the digit buffer out of snprintf() (the sign is added later).
// The null teminator is not counted in the snprintf() character count, but we need to count it when we allow storage space;
// Our 7 segment format has an implied plus + sign and leading zero suppression. examples: -0.898, -2.756, 4.675, 1000.098, 9066.456
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------
void format_float_7segment(unsigned char display, float num)
{
char digit_buff[10];
int i, k; // i indexes length of the digit buffer, k is used to clear left over digits from a longer string
char *bf; // gets the current digit from the digit_buff[] char buffer.
char v; // holds the value of the next digit to send to the display so we don't have to derefernce the pointer each time we act on the digit value.
//
snprintf(digit_buff,MAX_7SEG_DIGITS+1,"%1.3f",fabs(num)); // snprintf prints a maximum of 8 displayable digits plus a null terminator. Numbers less than one, have a decimal point with one leading zero.
if (strlen(digit_buff) > MAX_7SEG_DIGITS+1)
seven_seg_errors.too_many_digits = 1; // note, this count will include the decimal point a a character
strrev(digit_buff); // we reverse the digit order to make it easier to send to the display, right to left.(LSD to MSD)
bf =(char*)&digit_buff; // Assign a pointer to access the formatted digit_buff[] - the output of snprintf().
i = 1; // In the XC3714, digit 0 is indexed by the number 1
while(*bf) // Iterate the digit buffer until the null terminator is found.
{ // note: our call to snprintf() formats the absolute value of "num" --- fabs(num)
v = *bf; // get a digit.....
if (v == '.') {++bf; v = *bf++ | 0x80; } // if the character represents a decimal point, skip to the next digit and set its bit 7 to include the dp.
else v = *bf++; // else, just read the next digit from the digit buffer
write_7segment(i,v & 0x8F, display); // write the current digit (as BCD) to the display via SPI with its optional b7 flag (decimal point)
i++; // increment i to the next digit in the sequence (keep count).
} // once we are here, the formatted string has been output.
if (num < 0.0) write_7segment(i,0x0A, display); // next, optionally light the minus sign, (g segment)
else write_7segment(i,0x0F, display ); // then blank the next-but-one leading digit
for (k = i+1; k < 9; k++) write_7segment(k,0x0F,display); // now blank the remaining digits i.e. no 'leftover' or leading digits will appear on the display.
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------
void format_int_7segment(unsigned char display, short int num)
{
char digit_buff[11];
int i, k; // i indexes length of the digit buffer, k is used to clear left over digits from a longer string
char *bf; // gets the current digit from the digit_buff[] char buffer.
char v; // holds the value of the next digit to send to the display so we don't have to derefernce the pointer each time we act on the digit value.
//
snprintf(digit_buff,MAX_7SEG_DIGITS+2,"%d",abs(num)); // snprintf prints a maximum of 8 displayable digits plus a null terminator. Numbers less than one, have a decimal point with one leading zero.
if (strlen(digit_buff) > MAX_7SEG_DIGITS+1)
seven_seg_errors.too_many_digits = 1; // note, this count will include the decimal point a a character
strrev(digit_buff); // we reverse the digit order to make it easier to send to the display, right to left.(LSD to MSD)
bf =(char*)&digit_buff; // Assign a pointer to access the formatted digit_buff[] - the output of snprintf().
i = 1; // In the XC3714, digit 0 is indexed by the number 1
while(*bf) // Iterate the digit buffer until the null terminator is found.
{ // note: our call to snprintf() formats the absolute value of "num" --- fabs(num)
v = *bf; // get a digit.....
//.. if (v == '.') {++bf; v = *bf++ | 0x80; } // if the character represents a decimal point, skip to the next digit and set its bit 7 to include the dp.
v = *bf++; // else, just read the next digit from the digit buffer
write_7segment(i,v & 0x8F, display); // write the current digit (as BCD) to the display via SPI with its optional b7 flag (decimal point)
i++; // increment i to the next digit in the sequence (keep count).
} // once we are here, the formatted string has been output.
if (num < 0) write_7segment(i,0x0A, display); // next, optionally light the minus sign, (g segment)
else write_7segment(i,0x0F, display ); // then blank the next-but-one leading digit
for (k = i+1; k < 9; k++) write_7segment(k,0x0F,display); // now blank the remaining digits i.e. no 'leftover' or leading digits will appear on the display.
}
//--------------------------------------------------------------------------------------------
int write_7segment(unsigned char address, unsigned char data, unsigned char display)
{
T_XC3714_frame frame;
int response = 0;
uint16_t *p;
frame.bits = 0;
frame.address = address & 0x0F;
frame.data = data;
p = (uint16_t *) &frame;
switch(display)
{
case 0:ENABLE_7SEG_0 ; break;
case 1:ENABLE_7SEG_1 ; break;
case 2:ENABLE_7SEG_2 ; break;
}
SPI.beginTransaction(SPISettings(SPI_SPEED_7SEG, MSBFIRST, SPI_MODE0));
response = SPI.transfer16(*p); //
SPI.endTransaction();
switch(display)
{
case 0: DISABLE_7SEG_0; break;
case 1: DISABLE_7SEG_1; break;
case 2: DISABLE_7SEG_2; break;
}
return(response);
}
//----------------------------------------------------------------------------------------------------------
// does an inplace reverse of the characters in the argument string.
void strrev(char* str)
{
if (!str) { return; } // return immediately if the string has zero length (str is a NULL pointer)
// set up indexes to the start and end of the string
int i = 0; // start
int j = strlen(str) - 1; // end
// step through each character and reverse the string
while (i < j) {
char c = str[i]; // get the first char
str[i] = str[j]; // get the end char
str[j] = c; // insert the first char into the end char position
i++; // increment index of the first char.
j--; // decrement the index of the last char.
}
}
//--------------------------------------------------------------------------------------------
void init_XC3714 () // default initialisation for the XC3714 display driver chip
{
pinMode(CS_PIN0, OUTPUT);
pinMode(CS_PIN1, OUTPUT);
pinMode(CS_PIN2, OUTPUT);
SPI.begin();
write_7segment(SHUT_DOWN, 0x01, 0); // Shutdown - set display-enable bit
write_7segment(DECODE_MODE, 0xFF,0); // Decode mode - all digits are BCD formatted
write_7segment(SCAN_LIMIT, 0x07,0); // Scan limit - include all 8 digits to scan and display
write_7segment(INTENSITY, 0x01,0); // Display intensity control - set it to minimum
clear_7segment(0);
write_7segment(SHUT_DOWN, 0x01, 1); // Shutdown - set display-enable bit
write_7segment(DECODE_MODE, 0xFF,1); // Decode mode - all digits are BCD formatted
write_7segment(SCAN_LIMIT, 0x07,1); // Scan limit - include all 8 digits to scan and display
write_7segment(INTENSITY, 0x01,1); // Display intensity control - set it to minimum
clear_7segment(1);
write_7segment(SHUT_DOWN, 0x01, 2); // Shutdown - set display-enable bit
write_7segment(DECODE_MODE, 0xFF,2); // Decode mode - all digits are BCD formatted
write_7segment(SCAN_LIMIT, 0x07,2); // Scan limit - include all 8 digits to scan and display
write_7segment(INTENSITY, 0x01,2); // Display intensity control - set it to minimum
clear_7segment(2);
}
//---------------------------------------------------------------------------------------------
void clear_7segment(unsigned char display)
{
write_7segment(0x01,0x0F,display);
write_7segment(0x02,0x0F,display);
write_7segment(0x03,0x0F,display);
write_7segment(0x04,0x0F,display);
write_7segment(0x05,0x0F,display);
write_7segment(0x06,0x0F,display);
write_7segment(0x07,0x0F,display);
write_7segment(0x08,0x0F,display);
}
//---------------------------------------------------------------------------------------------
void pulse_7segment(uint8_t count, unsigned char display)
{
uint8_t i;
for (i = 0; i <count; i++)
{
write_7segment(0x01,0x0A,display); // minus sign
write_7segment(0x02,0x0A,display);
write_7segment(0x03,0x0A,display);
write_7segment(0x04,0x0A,display);
write_7segment(0x05,0x0A,display);
write_7segment(0x06,0x0A,display);
write_7segment(0x07,0x0A,display);
write_7segment(0x08,0x0A,display);
delay(100);
write_7segment(0x01,0x0F,display); // blank display
write_7segment(0x02,0x0F,display);
write_7segment(0x03,0x0F,display);
write_7segment(0x04,0x0F,display);
write_7segment(0x05,0x0F,display);
write_7segment(0x06,0x0F,display);
write_7segment(0x07,0x0F,display);
write_7segment(0x08,0x0F,display);
delay(100);
}
}
//--------------------------------------------------------------------------------------------
void set_dp_7segment(uint8_t pos)
{
//write_7segment(pos,0x8F);
}
//--------------------------------------------------------------------------------------------