/*==============================================================================
par Silicium628
derniere mise à jour 16 juin 2015
================================================================================
ATmega8
Pilotage d'un circuit DDS AD9850 générateur de fréquence 0..40MHz sinus avec un quartz (pour le AD9850) de 125MHz
-Affichage LCD 2 x 20 caractères ("splitté entre deux ports de l'ATmega)
================================================================================
*/
// #include <math.h>
#define F_CPU 16000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "dm_lcd.c"
#define pin_WCL 0b00010000 // sur port D
#define pin_FQU 0b10000000 // sur port D
#define pin_RESET 0b01000000 // sur port D
#define pin_DATA 0b00100000 // sur port D
char * version = "6.1";
uint32_t FTW; // le mot de 32 bits à transmettre au AD9951 en mode série sur le pin SDIO
uint32_t f_out; // fréquence du signal de sortie sinusoïdal
uint8_t pos; // position du multiplicateur
uint8_t pos_curseur; // position du curseur (pour affichage)
uint32_t pas;
uint8_t etat;
uint16_t compteur1;
uint32_t Frq_W32; // valeur binaire destine a l'AD9850, vaut environ 68,72 x frequence en Hz.
uint8_t phase;
uint8_t nb_cycles = 0;
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)
{
PORTB = 0b00001000; // PB3 = MOSI -> entrée ICPS
DDRB |= 0b11111111;
DDRC = 0b11111111;
DDRD = 0b11110000; // PD0..3 entrées codeurs rotatifs
PORTD = 0b00001111; // valide R de Pull Up sur les entrées
}
void InitINTs (void)
{
GICR |= 0b11000000; // gere les INTs - bit9 ->INT0 request - voir page 67 du pdf
MCUCR |= 0b00001010; // The falling edge of INT0 generates an interrupt request. The falling edge of INT1 generates an interrupt request. p:67 du pdf
}
void init_variables(void)
{
f_out = 1000;
pas = 100;
pos = 3;
}
void lcd_gotoxy_clrEOL (int x, int y)
// place le curseur en x,y et efface jusqu'a 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_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[8];
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 % 3) == 0 ) && (i>1)) { lcd_puts("."); } // separe les groupes de 3 chiffres
lcd_putc(tbl[nb_chiffres +1 -i]);
}
}
void lcd_aff_bin (unsigned long int valeur, int nb_digits)
{
//affiche un nombre en representation binaire
// 16 bits max
unsigned char 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_pas()
{
lcd_gotoxy_clrEOL(0, 3);
lcd_puts("PAS = ");
switch (pas)
{
case 1 : lcd_puts("1Hz");
break;
case 10 : lcd_puts("10Hz");
break;
case 100 : lcd_puts("100Hz");
break;
case 1000 : lcd_puts("1kHz");
break;
case 10000 : lcd_puts("10kHz");
break;
case 100000 : lcd_puts("100kHz");
break;
case 1000000 : lcd_puts("1MHz");
break;
case 10000000 : lcd_puts("10MHz");
break;
}
}
void affiche_POS()
{
lcd_gotoxy(0, 2);
lcd_puts("POS=");
lcd_aff_nb(pos, 2,0,0);
}
void affiche_frequence(uint8_t nb_chiffres)
{
lcd_gotoxy(0, 1);
lcd_puts("F=");
lcd_aff_nb_form3 (f_out, nb_chiffres);
lcd_puts(" Hz");
pos_curseur = nb_chiffres+4-pos;
if (pos > 3) {pos_curseur = nb_chiffres+3-pos;};
if (pos > 6) {pos_curseur = nb_chiffres+2-pos;};
lcd_gotoxy(pos_curseur, 1);
}
void reset_DDS(void)
{
_delay_ms(1);
PORTD |= pin_RESET;
_delay_ms(10);
PORTD &= ~pin_RESET; // l'operateur "~" donne le complement (inverse des bits). ne pas confondre avec l'opérateur "!"
_delay_ms(10);
}
void impulse_clk_W(void) // sur pin W_CL
{
_delay_us(5);
PORTD |= pin_WCL;
_delay_us(5);
PORTD &= ~pin_WCL; // l'operateur "~" donne le complement (inverse des bits). ne pas confondre avec l'opérateur "!"
_delay_us(5);
}
void impulse_FQ_U(void) // sur pin FQ_U
{
_delay_us(5);
PORTD |= pin_FQU;
_delay_us(5);
PORTD &= ~pin_FQU; // l'operateur "~" donne le complement (inverse des bits). ne pas confondre avec l'opérateur "!"
_delay_us(5);
}
void out_DDS(uint32_t freq_i) // 40 bits vers AD9850; voir son datasheet
{
uint8_t n;
//envoi de la frequence
uint32_t masque;
masque = 1;
for (n=0; n<= 31; n++) // on sort le LSB (W0) en premier
{
masque += masque; // revient à x2 le masque le '1' se deplacant de droite a gauche, 32 bits en tout
if ( (freq_i & masque) != 0) {PORTD |= pin_DATA;} else { PORTD &= ~pin_DATA; }
impulse_clk_W();
}
PORTD &= ~pin_DATA; // (W32 toujours = 0)
impulse_clk_W();
PORTD &= ~pin_DATA; // (W33 toujours = 0)
impulse_clk_W();
PORTD &= ~pin_DATA; // (W34 = Power-Down = 0)
impulse_clk_W();
// envoi de la phase (5 bits)
for (n=0; n<=4; n++) // on sort le LSB (W35) en premier et le MSB en dernier.
{
masque = (1 << n);
if (phase & masque) {PORTD |= pin_DATA;} else { PORTD &= ~pin_DATA; }
impulse_clk_W();
}
// envoi impulsion FQ_UD
impulse_FQ_U();
}
/***********************************************************************
f_out = FTW x CLKIN / 2³² nous dit le datasheet
en en déduit :
FTW = f_out * 2³² / CLKIN
ici CLKIN = 125MHz / 2 = 62.5 MHz
125MHZ c'est la fréquence du quartz
cette fréquence est /2 en interne
FTW = f_out * (2³² / 62.5E6 ) = 68.71947674 x f_out
************************************************************************/
void calcul_FTW()
{
float y;
y = 68.71947674 * f_out;
FTW = (uint32_t) y;
}
/***********************************************************************
// 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 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)
{
uint8_t n;
//interruption sur front descendant sur l'entree Int0
// declenchee par la rotation du codeur_ROT (2)
etat = PIND & 0b00000001;
if (etat == 0)
{
if (pos < 8) {pos++ ;}
}
else
{
if (pos > 1) {pos--;}
}
pas=1;
for (n=1; n<pos; n++)
{
pas *=10;
}
calcul_FTW();
out_DDS(FTW);
}
ISR (INT1_vect)
{
//interruption sur front descendant sur l'entree Int0
// declenchee par la rotation du codeur_ROT (1)
etat = PIND & 0b00000010;
if (etat == 0)
{
if (f_out >= pas) {f_out -= pas;}
}
else
{
if ( (f_out+pas) <= 50000000) {f_out += pas;}
if ( (f_out+pas) > 50000000) {f_out = 50000000;}
}
calcul_FTW();
out_DDS(FTW);
}
int main (void)
{
init_variables();
init_ports();
InitINTs();
reset_DDS();
lcd_init(LCD_DISP_ON_CURSOR);
lcd_clrscr();
lcd_home();
lcd_puts("Gene HF AD9850 v");
lcd_puts(version);
_delay_ms(2000);
lcd_clrscr();
lcd_gotoxy(13,0);
lcd_puts("AD9850");
sei(); // enable interruptions
while(1)
{
compteur1++;
if (compteur1 >= 10000)
{
affiche_pas();
// affiche_POS(); // pour test
affiche_frequence(8);
_delay_ms(10);
compteur1=0;
}
_delay_us(20);
}
}