Created
June 4, 2017 18:12
-
-
Save jeffThompson/10934cf8f07b3000e7e61601266b2b2c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Si4703 LIBRARY FOR ATTINY CHIPS | |
Jeff Thompson | 2017 | jeffreythompson.org | |
See .h file for credit where credit is due. | |
*/ | |
#include "Arduino.h" | |
#include "Si4703_ATtiny.h" | |
#include "TinyWireM.h" | |
Si4703_ATtiny::Si4703_ATtiny(int resetPin, int sdioPin, int sclkPin) { | |
_resetPin = resetPin; | |
_sdioPin = sdioPin; | |
_sclkPin = sclkPin; | |
} | |
void Si4703_ATtiny::powerOn() { | |
si4703_init(); | |
} | |
void Si4703_ATtiny::setChannel(int channel) { | |
// freq(MHz) = 0.200(in USA) * Channel + 87.5MHz | |
int newChannel = channel * 10; // 973 * 10 = 9730 | |
newChannel -= 8750; // 9730 - 8750 = 980 | |
newChannel /= 10; // 980 / 10 = 98 | |
// these steps come from AN230 page 20 rev 0.5 | |
readRegisters(); | |
si4703_registers[CHANNEL] &= 0xFE00; // clear out the channel bits | |
si4703_registers[CHANNEL] |= newChannel; // mask in the new channel | |
si4703_registers[CHANNEL] |= (1 << TUNE); // set the TUNE bit to start | |
updateRegisters(); | |
// poll to see if STC is set | |
while (1) { | |
readRegisters(); | |
if ( (si4703_registers[STATUSRSSI] & (1 << STC)) != 0) break; // tuning complete! | |
} | |
readRegisters(); | |
si4703_registers[CHANNEL] &= ~(1 << TUNE); // clear the tune after a tune has completed | |
updateRegisters(); | |
// wait for the si4703 to clear the STC as well | |
while (1) { | |
readRegisters(); | |
if ( (si4703_registers[STATUSRSSI] & (1 << STC)) == 0) break; // tuning complete! | |
} | |
} | |
int Si4703_ATtiny::seekUp() { | |
return seek(SEEK_UP); | |
} | |
int Si4703_ATtiny::seekDown() { | |
return seek(SEEK_DOWN); | |
} | |
void Si4703_ATtiny::setVolume(int volume) { | |
readRegisters(); // read the current register set | |
if (volume < 0) volume = 0; | |
if (volume > 15) volume = 15; | |
si4703_registers[SYSCONFIG2] &= 0xFFF0; // clear volume bits | |
si4703_registers[SYSCONFIG2] |= volume; // set new volume | |
updateRegisters(); // update | |
} | |
void Si4703_ATtiny::readRDS(char* buffer, long timeout) { | |
long endTime = millis() + timeout; | |
boolean completed[] = {false, false, false, false}; | |
int completedCount = 0; | |
while (completedCount < 4 && millis() < endTime) { | |
readRegisters(); | |
if (si4703_registers[STATUSRSSI] & (1 << RDSR)) { | |
// ls 2 bits of B determine the 4 letter pairs | |
// once we have a full set return | |
// if you get nothing after 20 readings return with empty string | |
uint16_t b = si4703_registers[RDSB]; | |
int index = b & 0x03; | |
if (! completed[index] && b < 500) { | |
completed[index] = true; | |
completedCount ++; | |
char Dh = (si4703_registers[RDSD] & 0xFF00) >> 8; | |
char Dl = (si4703_registers[RDSD] & 0x00FF); | |
buffer[index * 2] = Dh; | |
buffer[index * 2 + 1] = Dl; | |
} | |
delay(40); // wait for the RDS bit to clear | |
} | |
else { | |
delay(30); // from AN230, using the polling method 40ms should | |
// be sufficient amount of time between checks | |
} | |
} | |
if (millis() >= endTime) { | |
buffer[0] = '\0'; | |
return; | |
} | |
buffer[8] = '\0'; | |
} | |
// to get the Si4703 inito 2-wire mode, SEN needs to be high and SDIO needs to be low after a reset | |
// the breakout board has SEN pulled high, but also has SDIO pulled high | |
// therefore, after a normal power up the Si4703 will be in an unknown state | |
// RST must be controlled | |
void Si4703_ATtiny::si4703_init() { | |
pinMode(_resetPin, OUTPUT); | |
pinMode(_sdioPin, OUTPUT); // SDIO is connected to A4 for I2C | |
digitalWrite(_sdioPin, LOW); // a low SDIO indicates a 2-wire interface | |
digitalWrite(_resetPin, LOW); // put Si4703 into reset | |
delay(1); // some delays while we allow pins to settle | |
digitalWrite(_resetPin, HIGH); // bring Si4703 out of reset with SDIO set to low and | |
// SEN pulled high with on-board resistor | |
delay(1); // allow Si4703 to come out of reset | |
TinyWireM.begin(); // now that the unit is reset and I2C inteface mode, we need to begin I2C | |
readRegisters(); // read the current register set | |
si4703_registers[0x07] = 0x8100; // enable the oscillator, from AN230 page 9, rev 0.61 (works) | |
updateRegisters(); // update | |
delay(500); // wait for clock to settle - from AN230 page 9 | |
readRegisters(); // read the current register set | |
si4703_registers[POWERCFG] = 0x4001; // enable the IC | |
si4703_registers[SYSCONFIG1] |= (1 << RDS); // enable RDS | |
si4703_registers[SYSCONFIG1] |= (1 << DE); // 50kHz Europe setup | |
si4703_registers[SYSCONFIG2] |= (1 << SPACE0); // 100kHz channel spacing for Europe | |
si4703_registers[SYSCONFIG2] &= 0xFFF0; // clear volume bits | |
si4703_registers[SYSCONFIG2] |= 0x0001; // set volume to lowest | |
updateRegisters(); // update | |
delay(110); // max powerup time, from datasheet page 13 | |
} | |
// read the entire register control set from 0x00 to 0x0F | |
void Si4703_ATtiny::readRegisters() { | |
// Si4703 begins reading from register upper register of 0x0A and reads to 0x0F, then loops to 0x00 | |
// we want to read the entire register set from 0x0A to 0x09 = 32 bytes | |
TinyWireM.requestFrom(SI4703, 32); | |
// wait for 16 words/32 bytes to come back from slave I2C device | |
while (TinyWireM.available() < 32) ; | |
// we may want some time-out error here... | |
// remember, register 0x0A comes in first so we have to shuffle the array around a bit | |
for (int x = 0x0A ; ; x++) { // read in these 32 bytes | |
if (x == 0x10) x = 0; // loop back to zero | |
si4703_registers[x] = TinyWireM.read() << 8; | |
si4703_registers[x] |= TinyWireM.read(); | |
if (x == 0x09) break; // we're done! | |
} | |
} | |
// write the current 9 control registers (0x02 to 0x07) to the Si4703 | |
// it's a little weird, you don't write an I2C address - the Si4703 assumes | |
// you are writing to 0x02 first, then increments | |
byte Si4703_ATtiny::updateRegisters() { | |
TinyWireM.beginTransmission(SI4703); | |
// a write command automatically begins with register 0x02 so no need to send a write-to address | |
// first we send the 0x02 to 0x07 control registers | |
// in general, we should not write to registers 0x08 and 0x09 | |
for (int regSpot = 0x02 ; regSpot < 0x08 ; regSpot++) { | |
byte high_byte = si4703_registers[regSpot] >> 8; | |
byte low_byte = si4703_registers[regSpot] & 0x00FF; | |
TinyWireM.write(high_byte); // upper 8 bits | |
TinyWireM.write(low_byte); // lower 8 bits | |
} | |
// end this transmission | |
byte ack = TinyWireM.endTransmission(); | |
if (ack != 0) { // we have a problem! | |
return (FAIL); | |
} | |
return (SUCCESS); | |
} | |
// seeks out the next available station | |
// returns the freq if it made it, returns zero if failed | |
int Si4703_ATtiny::seek(byte seekDirection) { | |
readRegisters(); | |
// set seek mode wrap bit | |
si4703_registers[POWERCFG] |= (1 << SKMODE); // allow wrap | |
if (seekDirection == SEEK_DOWN) si4703_registers[POWERCFG] &= ~(1 << SEEKUP); // seek down is the default upon reset | |
else si4703_registers[POWERCFG] |= 1 << SEEKUP; // set the bit to seek up | |
si4703_registers[POWERCFG] |= (1 << SEEK); // start seek | |
updateRegisters(); // seeking will now start | |
// poll to see if STC is set | |
while (1) { | |
readRegisters(); | |
if ((si4703_registers[STATUSRSSI] & (1 << STC)) != 0) break; // tuning complete! | |
} | |
readRegisters(); | |
int valueSFBL = si4703_registers[STATUSRSSI] & (1 << SFBL); // store the value of SFBL | |
si4703_registers[POWERCFG] &= ~(1 << SEEK); // clear the seek bit after seek has completed | |
updateRegisters(); | |
// wait for the si4703 to clear the STC as well | |
while (1) { | |
readRegisters(); | |
if ( (si4703_registers[STATUSRSSI] & (1 << STC)) == 0) break; // tuning complete! | |
} | |
if (valueSFBL) { // the bit was set indicating we hit a band limit or failed to find a station | |
return (0); | |
} | |
return getChannel(); | |
} | |
// reads the current channel from READCHAN | |
// returns a number like 973 for 97.3MHz | |
int Si4703_ATtiny::getChannel() { | |
readRegisters(); | |
int channel = si4703_registers[READCHAN] & 0x03FF; // mask out everything but the lower 10 bits | |
// freq(MHz) = 0.100 (in Europe) * channel + 87.5MHz | |
// X = 0.1 * channel + 87.5 | |
// 98 + 875 = 973 | |
channel += 875; | |
return (channel); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment