T3 shop clock/radio
The TEA5767 works just fine with the Teensy3.x. Here is the complete program for a shop clock/radio that I developed using a Teensy 3.0. It uses a Nokia 5110 display and a mechanical encoder from SparkFun
https://www.sparkfun.com/products/9117. The program is a patchwork of sloppy code stitched together with good intentions. It includes temperature compensation for the Teensy RTC and a tweaked patch of code for the encoder. Again, the code is a bit sloppy and disorganized but it is reliable and works very well. I have not put everything in an enclosure yet so it is laced together on a small section of my bench and has worked great for over a year now.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <SPI.h>
#include <Wire.h>
#include <EEPROM.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <Time.h>
// These Adafruit(#338 blue board) Nokia 5110
// displays use SPI to communicate, 4 or 5 pins
// are required to interface.
// MCU pins -- Nokia pins
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 3 - LCD chip select (CS)
// pin 4 - LCD reset (RST)
// init display
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 3, 4);
/*****************************************************************************************************/
boolean toggle = false;
int SDIO = A4;
int SCLK = A5;
int channel;
int volume;
int myhour;
int mutePin = 1;
char tempString[10];
float fchannel;
const int encoderPin21 = 21; // encoder pin 1
const int encoderPin22 = 22; // encoder pin 2
#define INT_Pin21 21 // interrupt on #21
#define INT_Pin22 22 // interrupt on #22
//Volatile variables are needed if used within interrupts
volatile int lastEncoded = 0;
volatile long encoderValue = 0;
volatile int goodEncoderValue;
volatile boolean updateStation = false;
const boolean UP = true;
const boolean DOWN = false;
volatile boolean stationDirection;
/*****************************************************************************************************/
byte frequencyH = 0;
byte frequencyL = 0;
unsigned int frequencyB;
double frequency = 0;
/*****************************************************************************************************/
/*****************************************************************************************************/
void setup()
{
//setTime(hr,min,sec,day,mnth,yr)
//use 24 hour time in the above
//format with midnight being 0 hours
//PROCEDURE: un-comment the two lines below and
// upload the program. Then re-comment
// the same lines and upload the program again.
//setTime(14,58,0,23,9,14);
//Teensy3Clock.set(now());
hourFormat12();
// set the Time library to use Teensy 3.0's RTC to keep time
setSyncProvider(getTeensy3Time);
display.begin();
analogWrite(9,25); // turn on and modulate display backlight
display.setContrast(60); // set contrast
display.clearDisplay(); // clears the screen and buffer
Wire.begin();
read_channel_from_EEPROM();
if(channel <= 875 || channel >= 1081)
{ channel = 981;}
setFrequency();
update_display();
analogReadRes(12);
analogReadAveraging(8);
pinMode(mutePin,INPUT_PULLUP); // to read encoder momentary push switch
// both pins on the rotary encoder are inputs and pulled high
pinMode(encoderPin21, INPUT_PULLUP);
pinMode(encoderPin22, INPUT_PULLUP);
//call updateEncoder() when any high/low changed seen
//on interrupt 0 (pin 2), or interrupt 1 (pin 3)
attachInterrupt(INT_Pin21, updateEncoder, CHANGE);
attachInterrupt(INT_Pin22, updateEncoder, CHANGE);
updateEncoder();
}
/*****************************************************************************************************/
/*****************************************************************************************************/
void loop()
{
// interrupt tells us when to update the station
if(updateStation)
{
if(stationDirection == UP) channel += 2;
else if(stationDirection == DOWN) channel -= 2;
//Catch wrap conditions
if(channel > 1079) channel = 875;
if(channel < 875) channel = 1079;
setFrequency(); //Goto the new channel
save_channel(); // save channel to EEPROM
update_display();
updateStation = false; //Clear flag
}
if(!digitalRead(mutePin))
{
delay(100);
if(!digitalRead(mutePin))
{
toggle = !toggle;
setFrequency();
update_display();
}
while(!digitalRead(mutePin));
delay(100);
}
if(millis() % 200 == 0)update_display();
}
/*****************************************************************************************************/
/*****************************************************************************************************/
void setFrequency()
{
frequency = (double)channel/10;
frequencyB = 4 * (frequency * 1000000 + 225000) / 32768;
frequencyH = frequencyB >> 8;
frequencyL = frequencyB & 0XFF;
Wire.beginTransmission(0x60);
Wire.write(frequencyH);
Wire.write(frequencyL);
Wire.write(0xB2); // bit 1 forces mono mode and mutes right channel
if(!toggle)Wire.write(0x10); // use 0x58 for mute
else Wire.write(0x58);
Wire.write((byte)0x00);
Wire.endTransmission();
}
/*****************************************************************************************************/
/*****************************************************************************************************/
void updateEncoder()
{
delayMicroseconds(500);
int MSB = digitalRead(encoderPin21); //MSB = most significant bit
int LSB = digitalRead(encoderPin22); //LSB = least significant bit
int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
int sum = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011)
{
stationDirection = DOWN; //Counter clock wise
encoderValue--;
}
if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000)
{
stationDirection = UP; //Clock wise
encoderValue++;
}
lastEncoded = encoded; //store this value for next time
//Wait until we are more than 3 ticks from previous used value
if(abs(goodEncoderValue - encoderValue) > 3)
{
//The user can sometimes miss an indent by a count or two
//This logic tries to correct for that
//Remember, interrupts are happening in the background so encoderValue can change
//throughout this code
if(encoderValue % 4 == 0) //Then we are on a good indent
{
goodEncoderValue = encoderValue; //Remember this good setting
}
else if( abs(goodEncoderValue - encoderValue) == 3) //The encoder is one short
{
if(encoderValue < 0) goodEncoderValue = encoderValue - 1; //Remember this good setting
if(encoderValue > 0) goodEncoderValue = encoderValue + 1; //Remember this good setting
}
else if( abs(goodEncoderValue - encoderValue) == 5) //The encoder is one too long
{
if(encoderValue < 0) goodEncoderValue = encoderValue + 1; //Remember this good setting
if(encoderValue > 0) goodEncoderValue = encoderValue - 1; //Remember this good setting
}
updateStation = true;
}
}
/*****************************************************************************************************/
/*****************************************************************************************************/
time_t getTeensy3Time()
{
return Teensy3Clock.get();
}
/*****************************************************************************************************/
/*****************************************************************************************************/
void update_display(void)
{
// set up to read a LM335 temperature sensor with 12 bit resolution
// measured VCC was 3.279 volts so 1 count = .0008 volts. The LM335
// puts out 10 millivolts per deg. C. It is referenced to absolute
// zero so 0 degrees C will be 2.73 volts or 3412.5 counts. The next
// few lines convert the sensor output to degrees C.
float tmp_snsrC = analogRead(A9);
tmp_snsrC = (((tmp_snsrC - 3412.5) / 12.5) - 2.7);
//int tempcomp = abs((25.0 - (int)tmp_snsrC) / 2);
int tempcomp = abs(25.0 - (int)tmp_snsrC);
//Serial.println(tempcomp);
if(tempcomp >= 1) Teensy3Clock.compensate(tempcomp * -1);
// with Teensy3Clock.compensate(int), a positive number speeds the clock up
// and a negative number slows the clock down
fchannel = (float)channel/10;
sprintf(tempString, "%.1f", fchannel);
display.clearDisplay(); // clears the screen and buffer
display.setTextSize(2);
display.setTextColor(BLACK);
display.setCursor(0,1);
if(hour() <= 12 && hour() != 0) myhour = hour();
if(hour() >= 13) myhour = (hour() - 12);
if(hour() == 0) myhour = (hour() + 12);
if(myhour < 10)
{
display.print(" ");
display.print(myhour);
}
else display.print(myhour);
doDigits(minute());
if(isPM())display.print("PM");
else display.print("AM");
display.setTextSize(1);
display.print("--------------");
display.print(" RADIO CHANNEL");
//display.println(tmp_snsrC,1);
display.setTextSize(2);
display.setTextColor(BLACK);
if(toggle)
{
display.print(" MUTE");
display.display(); // sends buffer to display
}
else
{
if(channel < 1000) display.print(" ");
else display.print(" ");
display.println(tempString);
display.display(); // sends buffer to display
}
}
/*****************************************************************************************************/
/*****************************************************************************************************/
void save_channel()
{
int msb = channel >> 8; // move channel over 8 spots to grab MSB
int lsb = channel & 0x00FF; // clear the MSB, leaving only the LSB
EEPROM.write(1, msb); // write each byte to a single 8-bit position
EEPROM.write(2, lsb);
}
/*****************************************************************************************************/
/*****************************************************************************************************/
void read_channel_from_EEPROM()
{
int msb = EEPROM.read(1); // load the msb into one 8-bit register
int lsb = EEPROM.read(2); // load the lsb into one 8-bit register
msb = msb << 8; // shift the msb over 8 bits
channel = msb|lsb; // concatenate the lsb and msb
}
/*****************************************************************************************************/
/*****************************************************************************************************/
// utility function for digital clock display: prints preceding colon and leading 0
void doDigits(int digits)
{
display.print(":");
if(digits < 10)display.print('0');
display.print(digits);
}
/*****************************************************************************************************/