AA32 RADIO FM DUT GEII TOURS

Dependents:   RadioFM

Si4735.cpp

Committer:
jlpadiolleau
Date:
2019-12-16
Revision:
0:f5a073ecafa6

File content as of revision 0:f5a073ecafa6:

//Modifié par VG: attention il faut diminuer la vitesse du bus SPI sur la UnoRev3 (sinon gros problème de lecture)
//Voir ligne dans la méthode begin SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
//j'ai ajouté 1<<SPR0 pour mettre l'horloge / 16 (Fmax du Si7435=2,5MHz !!!)


/* Arduino Si4735 Library
 * Written by Ryan Owens for SparkFun Electronics 5/17/11
 * Altered by Wagner Sartori Junior 09/13/11
 * Actively Being Developed by Jon Carrier
 *
 * This library is for use with the SparkFun Si4735 Shield
 * Released under the 'Buy Me a Beer' license
 * (If we ever meet, you buy me a beer)
 *
 * See the header file for better function documentation.
 *
 * See the example sketches to learn how to use the library in your code.
*/

/*

SPCR
| 7    | 6    | 5    | 4    | 3    | 2    | 1    | 0    |
| SPIE | SPE  | DORD | MSTR | CPOL | CPHA | SPR1 | SPR0 |

SPIE - Enables the SPI interrupt when 1
SPE - Enables the SPI when 1
DORD - Sends data least Significant Bit First when 1, most Significant Bit first when 0
MSTR - Sets the Arduino in master mode when 1, slave mode when 0
CPOL - Sets the data clock to be idle when high if set to 1, idle when low if set to 0
CPHA - Samples data on the falling edge of the data clock when 1, rising edge when 0
SPR1 and SPR0 - Sets the SPI speed, 00 is fastest (4MHz) 11 is slowest (250KHz)

*/
#include "Si4735.h"


//This is just a constructor.
//Default values are assigned to various private variables
Si4735::Si4735()
{
	//busspi = new SPI(D11,D12,D13);//mosi,miso,sck
	SPISS = new DigitalOut(D10);
	GPO2 = new DigitalInOut(D2);//D2
	GPO1 = new DigitalInOut(D12);//D12
	POWER_PIN = new	DigitalOut(D8);//D8
	RADIO_RESET_PIN = new DigitalOut(D9);//D9
	
	_mode		= FM;//FM par défaut
	_locale	= EU;//europe de l'ouest
	_volume	= 63;//volume à fond
}

void Si4735::begin(char mode)
{
	_mode = mode;//FM par défaut
	//Etat RESET de l'ordinogramme
	//On configure les broches en sorties, positionne GPO1 et GPO2 à 1 pendant le reset du Si4735
	//pour se mettre en mode de communication SPI
	GPO2->output();//On force pour l'instant GPO2 en sortie
	GPO1->output();//On force pour l'instant GPO1 en sortie
	
	//On active la séquence reset du Si4735
	RADIO_RESET_PIN->write(0);
	POWER_PIN->write(0);
	//wait_ms(20);
	//On met un niveau 1 sur les GPO (mode SPI)
	GPO1->write(1);
	GPO2->write(1);
	wait_ms(20);
	//On alimente le Si4735
	POWER_PIN->write(1);
	wait_ms(20);
	//front montant sur la broche reset du Si4735 
	RADIO_RESET_PIN->write(1);
	wait_ms(200);//attente pour la prise en compte du mode choisi

	GPO1->input();//On remet GPO1 en entrée pour le bus SPI
	//broche INT_PIN en entrée
	GPO2->input();//On remet GPO2 en entrée
	//SS au niveau haut	
	SPISS->write(1);
	delete GPO1;//on détruit GPO1 pour laisser la broche D12 au bus SPI
	
	wait(0.2);
	//Configure le SPI, Maitre, 1MHz Mode 0 à priori pour le Si4735
	//2,5 MHz max pour l'horloge bus SPI
	busspi = new SPI(D11,D12,D13);//mosi,miso,sck
	busspi->format(8,0);//8 bits de données et mode 0
	busspi->frequency(1000000);//1 MHz
	wait(0.2);
	
	//Emission de la commande POWER_UP (0x01)
	sprintf(command, "%c%c%c", 0x01, 0x50, 0x05);//Quartz 32768Hz externe validé et sortie GPO2, sortie analogique validé
	sendCommand(command, 3);
	wait_ms(200);//Pas vu dans la doc ???

	//Configure GPO lines to maximize stability
	sprintf(command, "%c%c", 0x80,0x06);//GPO1 et GPO2 en sortie, GPO3 en haute impédance
	sendCommand(command, 2);
	wait_ms(10);
	sprintf(command, "%c%c", 0x81,0x04);//toutes les sorties à zéro sauf GPO1 (pourquoi MISO à 1 ?)
	sendCommand(command, 2);
	wait_ms(10);

	//Configure le volume à la valeur courante
	setVolume(_volume);

	//son sur les sorties audio
	unmute();
         
    setLocale(EU);//désaccentuation Europe = 50µs (filtrage)
    /*    
	//Enable RDS
	setProperty(0x1500, 0x0001);//positionne RDSINT quand la pile FIFO a des données RDS
	setProperty(0x1501, 0x0004);//Minimum 4 groupes RDS stocké dans la FIFO (A,B,C et D)
	//setProperty(0x1502, 0xEF01);//Validation RDS et config de la gestion des erreurs sur chaque bloc
	//Only store good blocks and ones that have been corrected
	setProperty(0x1502, 0xAA01);	
	//Only store good blocks
	//setProperty(0x1502, 0x0001);//Validation RDS et aucune erreur sur les blocs
	*/
    seekThresholds(2,14);//on ajuste le seuil pour rechercher plus de stations SNR=2, RSSI=14
}


void Si4735::tuneFrequency(uint16_t frequency)
{
	//Split the desired frequency into two character for use in the
	//set frequency command.
	uint8_t highByte = frequency >> 8;
	uint8_t lowByte = frequency & 0x00FF;
	
	//set the new frequency. Mode FM uniquement
	sprintf(command, "%c%c%c%c", 0x20, 0x00, highByte, lowByte);
	sendCommand(command, 4);
	wait_ms(100);
}
#if defined(USE_SI4735_REV)
void Si4735::getREV(char*FW,char*CMP,char *REV)
{
	//FW = Firmware and it is a 2 character array
	//CMP = Component Revision and it is a 2 character array
	//REV = Chip Revision and it is a single character
	char response [16]={0};
	sprintf(command, "%c", 0x10);
	
	//Send the command
	sendCommand(command, 1);

	//Now read the response	
	getResponse(response);	

	FW[0]=response[2];
	FW[1]=response[3];
	FW[2]='\0';
	CMP[0]=response[6];
	CMP[1]=response[7];
	CMP[2]='\0';
	*REV=response[8];	
}
#endif //USE_SI4735_REV

uint16_t Si4735::getFrequency()
{
	char response [16]={0};
	uint16_t frequency=0;	
	uint8_t highByte=0;
	uint8_t lowByte=0;
			
	//The FM_TUNE_STATUS command
	sprintf(command, "%c%c", 0x22, 0x00);
	//Send the command
	sendCommand(command, 2);

	//Now read the response	
	getResponse(response);
	
	lowByte=response[3];
	highByte=response[2];
        
    frequency = (highByte<<8)+lowByte;
    return frequency;
}

void Si4735::seekUp(void)
{
	//Use the current mode selection to seek up.
	sprintf(command, "%c%c", 0x21, 0x0C);
	sendCommand(command, 2);
	wait_ms(1);
}

void Si4735::seekDown(void)
{
	//Use the current mode selection to seek down.
	sprintf(command, "%c%c", 0x21, 0x04);
	sendCommand(command, 2);	
	wait_ms(1);
}

void Si4735::seekThresholds(uint8_t SNR, uint8_t RSSI)
{
	//Use the current mode selection to set the threshold properties.	
	if(SNR>127)SNR=127;
	else if(SNR<1)SNR=0;
	if(RSSI>127)RSSI=127;
	else if(RSSI<1)RSSI=0;
	setProperty(0x1403, (uint16_t)SNR);	
	setProperty(0x1404, (uint16_t)RSSI);				
}

#if defined(USE_SI4735_RSQ) 
void Si4735::getRSQ(Metrics * RSQ)
{
	//This function gets the Received Signal Quality Information
	char response [16]={0};
				
	//The FM_RSQ_STATUS command
	sprintf(command, "%c%c", 0x23, 0x00);
	//Send the command
	sendCommand(command, 2);

	//Now read the response	
	getResponse(response);	

	//Pull the response data into their respecive fields
	RSQ->RSSI=response[4];
	RSQ->SNR=response[5];

	RSQ->STBLEND=response[3]&63;
	RSQ->MULT=response[6];
	RSQ->FREQOFF=response[7];
}
#endif

uint8_t Si4735::volumeUp(void)
{
	//63 est le volume maximum
	if(_volume < 63)
	{
		_volume+=1;
		//Set the volume to the current value.
		setProperty(0x4000, (uint16_t)_volume);	
	}
	return _volume;
}

uint8_t Si4735::volumeDown(void)
{
	//If we're not at the minimum volume yet, decrease the volume
	if(_volume > 0){
		_volume-=1;
		//Set the volume to the current value.
		setProperty(0x4000, (uint16_t)_volume);	
	}
	return _volume;
}

uint8_t Si4735::setVolume(uint8_t value)
{
	if(value <= 63 && value > 0){
		_volume=value;
		//Set the volume to the current value.
		setProperty(0x4000, (uint16_t)_volume);
	}
	return _volume;
}

uint8_t Si4735::getVolume(void)
{	
	return _volume;
}

void Si4735::mute(void)
{
	setProperty(0x4001, 0x0003);//coupe le son sur les voies gauche et droite
}

void Si4735::unmute(void)
{
	setProperty(0x4001, 0x0000);//son L et R ok
}

char Si4735::getStatus(void)
{
	char response=0;
	//SS au niveau bas
	SPISS->write(0);
	wait_ms(1);
	spiTransfer(0xA0);  //Commande de lecture sur GPO1 d'un octet (MISO du SPI)
	wait_ms(1);
	response = spiTransfer(0x00);  //Lecture de la réponse
	//SS au niveau haut, fin de lecture
	SPISS->write(1);
	return response;
}

void Si4735::getResponse(char *response)
{
	//char cmd=0xE0;
	//SS au niveau bas
	SPISS->write(0);
	wait_ms(1);
	busspi->write(0xE0);//Commande de lecture d'une série de 16 octets sur GPO1 (MISO)
	//spiTransfer(0xE0);  //Commande de lecture d'une série de 16 octets sur GPO1 (MISO)
	//wait_ms(1);
	//lecture de 16 octets - le premier est l'octet de contrôle, puis les 15 data
	//busspi->write(&cmd,1,response,16);
	for(int i=0; i<16; i++) *response++ = busspi->write(0x00);  //Lecture des 16 octets
	//SS au niveau haut, fin de lecture
	SPISS->write(1);
}

void Si4735::end(void)
{
	sprintf(command, "%c", 0x11);//Commande POWER_DOWN
	sendCommand(command, 1);
	wait_ms(1);
}

void Si4735::setLocale(uint8_t locale)
{
	_locale=locale;	
	//Set the deemphasis to match the locale
	switch(_locale)
	{
		case NA:			
			setProperty(0x1100, 0x0002);		
			break;
		case EU:
			setProperty(0x1100, 0x0001);
			break;
		default:
			break;
	}
}

uint8_t Si4735::getLocale(void)
{
	return _locale;
}


void Si4735::setMode(char mode)
{
	end();
	begin(mode);
}

char Si4735::getMode(void)
{
	return _mode;
}

//Envoie la commande 0x12 pour positionner la valeur value du paramètre situé à l'adresse address
void Si4735::setProperty(uint16_t address, uint16_t value)
{	
	sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, (address>>8)&255, address&255, (value>>8)&255, value&255);
	sendCommand(command, 6);
	wait_ms(1);
}

//Envoie la commande 0x13 pour lire la valeur du paramètre situé à l'adresse address.
uint16_t Si4735::getProperty(uint16_t address)
{	
	char response [16]={0};	
	sprintf(command, "%c%c%c%c", 0x13, 0x00, (address>>8)&255, address&255);
	sendCommand(command, 4);
	getResponse(response);
	return response[2]<<8 | response[3];
}

/*******************************************
*
* Private Functions
*
*******************************************/
//Ecriture ou lecture sur le bus SPI !
char Si4735::spiTransfer(char value)
{
	return busspi->write(value);//Emission d'un octet ou lecture
}

//Ecriture de données dans le Si4735
//length=8 octets maximum
void Si4735::sendCommand(char * command, int length)
{
  //SS au niveau bas
  SPISS->write(0);
  wait_ms(1);
  spiTransfer(0x48);  //Octet de contrôle à 0x48 pour signifier une écriture dans le Si4735 (puis envoie de 8 octets de data)
  for(int i=0; i<length; i++)spiTransfer(command[i]);
  for(int i=length; i<8; i++)spiTransfer(0x00);  //on compléte avec des 0 si la data est < à 8 octets.
  //Fin de la séquence par SS au niveau haut
  SPISS->write(1);
}