/*
Récepteur radio basé sur un module TEA5767
* Version A du firmware -> pour afficheur LCD 2x16c
*/
#define F_CPU 3276800
#include <util/delay.h>
#include <avr/io.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <avr/interrupt.h>
#include "dm_lcd3.cpp" // l'attribution des pins/ports pour le LCD (et le type de LCD) est faite dans le fichier "dm_lcd.h"
#include "lcd_curseur_628.cpp"
#include "I2C_628.c"
char* version = "version 2.1";
uint16_t f_out; // 880..1080 ; 894 -> pour 89.4MHz // incrémentable par le bouton rotatif
uint8_t freq_H = 0; // pour la PLL
uint8_t freq_L = 0; // pour la PLL
uint8_t num_station;
uint8_t nb_stations;
uint16_t pos_curseur; // 0..80
uint8_t status = 0;
uint8_t demande_calcul;
uint8_t etat1, etat2;
uint8_t mode; // 0 = sélection par fréquence; 1 = sélection par stations
uint16_t compteur1;
//double freq_available=0;
uint8_t TEA5767_buffer[5]={0x00,0x00,0xB0,0x10,0x00};
uint8_t addr1= 0 ; // 0x60;
struct station
{
uint16_t frq;
char nom[10+1];
};
struct station liste_stations[20]; // 0..19
void init_ports (void) // ports perso
// 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
{
DDRB |= 0b11111111;
PORTB = 0b00000000;
DDRC = 0b11001111; // bit4=SDA ; bit5=SLC
PORTC = 0b00110000;
DDRD = 0b11101010; // portD[0] entrée codeur_ROT ; portD[2] entrée INT0 ; portD[4] = entrée bouton "MODE"
PORTD = 0b00010101; // enable R de rappel sur ports en entrée
}
void InitINTs (void)
{
GICR |= 0b11000000; // gere les INTs - voir page 67 du pdf
MCUCR |= 0b00000010; // The falling edge of INT0 generates an interrupt request. p:67 du pdf
TWCR |= 0b00000001; // Bit 0 – TWIE: TWI Interrupt Enable - p:192
}
void init_variables(void)
{
num_station =0;
f_out = liste_stations[num_station].frq;
mode = 1;
demande_calcul = 1;
}
void init_TWI()
{
// Initialise le périphérique TWI
TWSR = 0x00; // Select Prescaler 1/1 // voir Tableau 20-10. TWI Bit Rate Prescaler p:193
TWBR =72; // SCL frequency = 16000000 / (16 + 2 * 72 * 1) = 100.000 khz
}
void init_lcd()
{
uint8_t i;
lcd_init(LCD_DISP_ON_CURSOR); // cursor on
//cd_init(LCD_DISP_ON); // cursor off
lcd_cree_5_caract_echelle();
lcd_clrscr();
}
void init_stations()
{
nb_stations = 13; // max 19
strcpy(liste_stations[0].nom , "Inter");
liste_stations[0].frq = 894;
strcpy(liste_stations[1].nom , "Musique");
liste_stations[1].frq = 929;
strcpy(liste_stations[2].nom , "RFM");
liste_stations[2].frq = 956;
strcpy(liste_stations[3].nom , "Culture");
liste_stations[3].frq = 978;
strcpy(liste_stations[4].nom , "RF-HERAULT");
liste_stations[4].frq = 1011;
strcpy(liste_stations[5].nom , "Perso");
liste_stations[5].frq = 885;
strcpy(liste_stations[6].nom , "Fun Radio");
liste_stations[6].frq = 918;
strcpy(liste_stations[7].nom , "Rd Clapas");
liste_stations[7].frq = 935;
strcpy(liste_stations[8].nom , "Cherie FM");
liste_stations[8].frq = 969;
strcpy(liste_stations[9].nom , "FIP");
liste_stations[9].frq = 997;
strcpy(liste_stations[10].nom , "Nostalgie");
liste_stations[10].frq = 1039;
strcpy(liste_stations[11].nom , "Fr Info");
liste_stations[11].frq = 1051;
strcpy(liste_stations[12].nom , "Rd Classiq");
liste_stations[12].frq = 1073;
}
void lcd_gotoxy_clrEOL (int x, int y)
// place le curseur en x,y et efface jusqu'à la fin de la ligne
{
lcd_gotoxy(x, y);
uint8_t i;
for (i=x; i<20; i++){ lcd_puts(" "); }
lcd_gotoxy(x, y);
}
void lcd_aff_nb (uint32_t valeur, uint8_t nb_chiffres, uint8_t nb_decimales, uint8_t affi_zeros)
{
//affiche un nombre en representation decimale
// affi_zeros = 1 affiche les zéros non significatifs à gauche.
unsigned char r ;
char tbl[7];
char digit;
uint8_t i;
for (i=1; i<=nb_chiffres; i++)
{
r=48 + valeur % 10; // modulo (reste de la division)
valeur /= 10; // quotient
tbl[i]=r;
}
uint8_t debut = 1-affi_zeros; // pour ne pas afficher des zeros à gauche du 1er chiffre
for (i=1; i<=nb_chiffres; i++)
{
if (i== (nb_chiffres - nb_decimales +1) ) { lcd_puts("."); }
digit = tbl[nb_chiffres +1 -i];
if (digit != '0') {debut = 0;}
if ((digit != '0') || (debut == 0)) {lcd_putc(digit);}
}
}
void lcd_aff_nb_relatif (int16_t valeur, uint8_t nb_chiffres, uint8_t nb_decimales)
{
//affiche un nombre relatif (c.a.d positif ou négatif) en representation decimale
unsigned char r ;
char tbl[7];
char digit;
uint8_t i;
uint8_t affi_zeros = 1; // affi_zeros = 1 affiche les zéros non significatifs à gauche.
if (valeur <0)
{
valeur = -valeur;
lcd_putc('-');
}
else
{
lcd_putc('+');
}
for (i=1; i<=nb_chiffres; i++)
{
r=48 + valeur % 10; // modulo (reste de la division)
valeur /= 10; // quotient
tbl[i]=r;
}
uint8_t debut = 1-affi_zeros; // pour ne pas afficher des zeros à gauche du 1er chiffre
for (i=1; i<=nb_chiffres; i++)
{
if (i== (nb_chiffres - nb_decimales +1) ) { lcd_puts("."); }
digit = tbl[nb_chiffres +1 -i];
if (digit != '0') {debut = 0;}
if ((digit != '0') || (debut == 0)) {lcd_putc(digit);}
}
}
void lcd_aff_nb_form3 (uint32_t valeur, uint8_t nb_chiffres)
{
//affiche un nombre en representation decimale en separant les groupes de 3 chiffres
unsigned char r ;
char tbl[7];
uint8_t i;
for (i=1; i<=nb_chiffres; i++)
{
r=48 + valeur % 10; // modulo (reste de la division)
valeur /= 10; // quotient
tbl[i]=r;
}
for (i=1; i<= nb_chiffres; i++)
{
if ((((i-1) % 3) == 0 ) && (i>1)) { lcd_puts("."); }
lcd_putc(tbl[nb_chiffres +1 -i]);
}
}
void lcd_aff_bin (uint16_t valeur, uint8_t nb_digits)
{
//affiche un nombre en representation binaire
// 16 bits max
uint8_t r ;
char tbl[17];
uint8_t i;
for (i=1; i<=nb_digits; i++)
{
r= 48 + valeur % 2; // modulo (reste de la division)
valeur /= 2; // quotient
tbl[i]=r;
};
for (i=1; i<=nb_digits; i++)
{
lcd_putc(tbl[nb_digits +1 -i]);
}
}
void affiche_echelle()
{
// Trace l'échelle graduée
uint8_t i;
lcd_gotoxy(0,1);
for(i = 0; i < 15; i++) { lcd_putc(CARACT_GRADUATION); }
}
void affiche_frequence()
{
uint8_t i;
uint8_t Ok=0;
lcd_gotoxy_clrEOL (0, 0);
lcd_aff_nb(f_out, 4, 1, 0);
// if (mode == 0) { lcd_puts(" MHz"); }
lcd_puts(" ");
for (i=0; i<=nb_stations; i++)
{
if (f_out == liste_stations[i].frq )
{
lcd_puts (liste_stations[i].nom);
Ok=1;
break;
}
}
if (Ok == 0) {lcd_puts("MHz ");}
pos_curseur = (f_out-880) * 4 / 10;
affiche_curseur2(pos_curseur);
}
void affiche_num_station()
{
lcd_gotoxy(15,0);
if (mode == 0) { lcd_gotoxy(15,0); lcd_putc('F'); }
else
{
lcd_gotoxy(14,1);
lcd_aff_nb (num_station, 2, 0, 1);
}
}
/***********************************************************************
// traitement du codeur_rot()
// codeur incrémental code Gray avec en plus fonction bouton poussoir
// le codeur rotatif utilisé est toujours à 11 en position stable
// dans le sens CW il fait 11->01->00->10->11 et par conséquent dans le sens CCW il fait 11->10->00->01->11
// 11
// 01
// 00
// 10
// 11
// il suffit donc de connecter un bit sur le pin INT0 (PD2) et de déclencher l'INT sur front descendant (passage de 0 à 1)
// cette INT lira alors l'état de l'autre bit (connecté à PD0 par exemple). Cet état diffère suivant le sens de rotation
************************************************************************/
ISR(BADISR_vect)
{
// évite de planter si une int est enable et pas de procedure associée écrite (ce qui fait reseter l'ATmega)
}
ISR (INT0_vect)
{
//interruption sur front descendant sur l'entree Int0
// déclenchee par la rotation du codeur_ROT (1) -> +/-fréquence ou +/-trim ajustement fréquence 8MHz pour étalonnage par GPS
etat1 = PIND & 0b00000001;
if(mode==0)
{
if (etat1 == 0)
{
if ( f_out >= 880) {f_out -= 1;}
if ( f_out < 880)
{
f_out = 880;
mode = 1;
}
}
else
{
if ( f_out <= 1080) {f_out += 1;}
if ( f_out > 1080)
{
f_out = 1080;
mode = 1;
}
}
affiche_frequence();
demande_calcul = 1;
}
if(mode==1)
{
if (etat1 == 0)
{
if ( num_station > 0) {num_station -= 1;}
}
else
{
if ( num_station < nb_stations) {num_station += 1;}
if ( num_station > (nb_stations-1))
{
num_station = nb_stations;
f_out = 880;
mode = 0;
}
}
if (mode==1) { f_out = liste_stations[num_station].frq; }
affiche_echelle();
affiche_frequence();
demande_calcul = 1;
}
}
void envoi_frequence(uint8_t addr_i)
{
uint8_t r1;
/**
Writing to TEA5767
Radio chip is controlled by writing 5 bytes one by one.
During writing TEA5767 IC2 address is 0x60, reading – 0x61.
Frequency is controlled by 14 bit word, that is written to two 8 bit registers.
**/
uint8_t datas[5]; // tableau
datas[0]=freq_H;
datas[1]=freq_L; // bit7 = MUTE ; bit6 = search mode
datas[2]=0xB0; // high side LO injection is on
datas[3]=0x10; // Xtal is 32.768 kHz
datas[4]=0x00;
r1=i2c_write_n_bytes(addr_i, datas, 5); // Passage du tableau en paramètre (on n'écrit ni indice ni étoile dans le cas d'un tableau)
}
calcul_frequence_out()
{
float f1;
uint32_t f2;
f1 = (float) f_out / 10;
f2=4*(f1 * 1000000 + 225000) / 32768 +1; //calcul du mot pour la PLL
freq_H=f2 >> 8;
freq_L=f2 & 0xFF;
}
int main()
{
uint8_t addr1;
init_stations();
init_variables();
init_ports();
InitINTs();
init_lcd();
sei(); // enable interruptions
lcd_clrscr();
lcd_puts("Radio FM TEA5767");
lcd_gotoxy(0,1);
lcd_puts(version);
_delay_ms(100);
init_TWI();
calcul_frequence_out();
TWCR = (1<<TWEN); //enable TWI
envoi_frequence(0x60);
_delay_ms(2);
// lcd_command(LCD_DISP_ON); // cursor off
lcd_clrscr();
affiche_echelle();
affiche_frequence();
//PORTB |= 0b00000010;
while(1)
{
if (demande_calcul == 1)
{
demande_calcul=0;
calcul_frequence_out();
envoi_frequence(0x60);
// if (mode == 1) {affiche_num_station();}
affiche_num_station();
}
etat2 = PIND & 0b00010000; // bouton MODE
if (etat2 != 0b00010000)
{
mode ^= 0b00000001;
lcd_clrscr();
_delay_ms(10);
// if (mode == 0) {PORTB &= 0b11111101;} else {PORTB |= 0b00000010;} // LED
demande_calcul=1;
affiche_echelle();
affiche_frequence();
_delay_ms(10);
}
_delay_ms(2);
}
}