/*==============================================================================
par Silicium628
derniere mise à jour 4 aout 2014
================================================================================
ATmega8
Pilotage d une alimentation a decoupage 20V
-Affichage LCD 2 x 16 caractères
================================================================================
IMPORTANT: programmer les FUSES BODLEVEL et BODEN (Brown out level detector) afin de fiabiliser le RESET à l'allumage
*/
#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include "dm_lcd.c" // l'attribution des pins/ports pour le LCD (et le type de LCD) est faite dans le fichier "dm_lcd.h"
#define bouton_L 0b00000001
// #define bouton_U 0b00000010
// #define bouton_D 0b00000100
#define bouton_R 0b00001000
char * version = "6.6";
/*
RAPPEL variables avr-gcc (vérifiable avec le .map)
char 1 -128 .. 127 ou caractères
unsigned char 1 0 .. 255 (equiv à byte du pascal)
uint8_t 1 (c'est la même chose que l'affreux 'unsigned char')
char toto[n] n
int 2 -32768 .. 32767
int16_t 2 idem 'int'
short int 2 pareil que int (?)
unsigned int 2 0 .. 65535
uint16_t 2 idem 'unsigned int'
long int 4 -2 147 483 648 à 2 147 483 647
int32_t 4 32 bits ; idem long int
unsigned long long 64-bit unsigned type.
uint64_t 8 64-bit unsigned type.
long long int 8
unsigned long int 4 32 bits ; 0 .. 4 294 967 295 (4,2 x 10^9)
float 4
double 4 // (?)
La déclaration char JOUR[7][9];
réserve l'espace en mémoire pour 7 mots contenant 9 caractères (dont 8 caractères significatifs).
*/
uint16_t consigne_V; // 0..200 pour 0V.. a 20.0V
uint16_t valeur16b;
// uint8_t digits_consigne_V[3];
uint16_t I_max;
// uint16_t mesure_V;
uint16_t memo1_I;
uint16_t memo2_I;
uint16_t memo3_I;
uint16_t memo4_I;
uint16_t mesure_I;
uint16_t valeur_I;
uint8_t pos;
uint8_t pos_curseur;
uint8_t boutons_I;
uint8_t boutons_V;
uint8_t memo_boutons_V;
uint8_t code_rot;
uint8_t memo_code_rot;
uint8_t etat;
int16_t valeur_rot;
uint16_t compteur1;
uint16_t offset_I;
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 = 0b00000000;
DDRB |= 0b00000010; // portB[1] = sortie (OC1A = sortie PWM 16 bits du timer1)
DDRC = 0b11001111; //PC4 en entree (ADC4 - mesure du courant); PC5 en entree (ADC5 - mesure de la tension)
DDRD = 0b11000000; //portD[0..3] en entree (4 boutons) ; portD[4..5] en entree (4 boutons sur 2 bits)
PORTD = 0b00111111;
}
void InitADC (void)
{
ADCSRA = _BV(ADEN) | _BV(ADPS2); // Activate ADC with Prescaler 16 --> 1Mhz/16 = 62.5kHz
ADMUX |= 0b11000101; //Bit 7:6 – REFS1:0: ADC Reference Selection Bits =11 -> Internal 2.56V Voltage Reference with external capacitor at AREF pin ; Bits 0:3 - Analog Channel Selection Bits
// ici Select pin ADC5 using MUX avec ref tension interne = 2.56V
}
void InitINTs (void)
{
GICR |= 0b00000000; // gere les INTs voir page 67 du pdf
MCUCR |= 0b00000010; // The falling edge of INT0 generates an interrupt request. p:67 du pdf
}
void InitPWM (void)
{
TCCR1A |= (1 << COM1A1); // set none-inverting mode
TCCR1A |= (1 << WGM11) | (1 << WGM10); // set 10bit phase corrected PWM Mode
// TCCR1B |= (1 << CS11); // set prescaler to 8 and starts PWM
TCCR1B |= (1 << CS10); // set no prescaler and starts PWM
OCR1A = 0;
}
void init_variables(void)
{
consigne_V=0;
offset_I =0;
memo1_I=0;
memo2_I=0;
memo3_I=0;
memo4_I=0;
valeur_rot=100;
}
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 (uint16_t valeur, uint8_t nb_chiffres, uint8_t nb_decimales)
{
//affiche un nombre en representation decimale
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== (nb_chiffres - nb_decimales +1) ) { lcd_puts("."); }
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_consigne_V(void)
{
// void lcd_aff_nb (uint16_t valeur, uint8_t nb_chiffres, uint8_t nb_decimales)
lcd_gotoxy_clrEOL (0,0);
lcd_puts("V: ");
lcd_aff_nb(consigne_V, 3, 1);
lcd_puts("V");
lcd_gotoxy(pos_curseur,0);
}
/*
void affiche_mesure_V(void)
{
// void lcd_aff_nb (uint16_t valeur, uint8_t nb_chiffres, uint8_t nb_decimales)
lcd_gotoxy_clrEOL (6,0);
lcd_puts("mesu:");
lcd_aff_nb(mesure_V, 3, 1);
lcd_puts("V");
lcd_gotoxy(pos_curseur,0);
}
*/
void affiche_valeur_I(valeur)
{
lcd_gotoxy_clrEOL (0,1);
lcd_puts("I=");
lcd_aff_nb(valeur, 4, 0);
lcd_puts("mA");
lcd_gotoxy(pos_curseur,0);
}
void affiche_offset_I(valeur)
{
lcd_gotoxy_clrEOL (9,1);
lcd_aff_nb(valeur, 4, 0);
lcd_gotoxy(pos_curseur,0);
}
void affiche_consigne_I(void)
{
uint16_t i_A;
i_A = I_max / 1000;
lcd_gotoxy_clrEOL (6,0);
lcd_puts("Imax=");
lcd_aff_nb(i_A, 1, 0);
lcd_puts("A");
lcd_gotoxy(pos_curseur,0);
}
/*
void affiche_consigne_V(void)
{
uint8_t c, i, p;
lcd_gotoxy_clrEOL (0,0);
for (i=0; i<=2; i++)
{
p=2-i;
c=48+digits_consigne_V[p];
if ((p==2) & digits_consigne_V[p] ==0 )
{
lcd_putc(' '); // n'affiche pas le premier 0 non sinificatif
}
else {lcd_putc(c); }
if (p==1) {lcd_putc('.');}
}
lcd_puts("V");
pos_curseur =2-pos;
if (pos_curseur>1) {pos_curseur++;}
lcd_gotoxy(pos_curseur,0);
}
*/
/*
void acqui_tension(void)
{
ADMUX = 0b11000101; // Select pin ADC5 using MUX - ref tension interne = 2.56V
ADCSRA |= _BV(ADSC); //Start conversion - resolution 10bits
while (ADCSRA & _BV(ADSC) ) {} // attend la fin de la converstion
// ADCW = 0..1024 pour Vin = 0..2V56
mesure_V = ADCW;
}
*/
void acqui_courant(void)
{
ADMUX = 0b11000100; // Select pin ADC4 using MUX - ref tension interne = 2.56V
ADCSRA |= _BV(ADSC); //Start conversion - resolution 10bits
while (ADCSRA & _BV(ADSC) ) {} // attend la fin de la converstion
//mesure_I = (894-ADCW)*16; // en mA ; lit la valeur convertie ; ADCW = 0..1024 pour Vin = 0..2V56
mesure_I = ADCW;
}
void lit_boutons_I(void)
{
boutons_I = PIND & 0b00110000;
if (boutons_I == 0b00000000) {I_max = 4000; }
if (boutons_I == 0b00010000) {I_max = 3000; }
if (boutons_I == 0b00100000) {I_max = 2000; }
if (boutons_I == 0b00110000) {I_max = 1000; }
}
void out_V()
{
valeur16b = 220 + consigne_V * 94;
OCR1A = valeur16b / 32; // fixe le rapport cyclique du signal PWM qui, une fois intégré par une cellule RC, produira la tension de consigne
affiche_consigne_V();
}
void test_codeur_rot()
{
// pour test des états (diffèrent suivant modèles) du codeur rotatif pas à pas code gray
// le mien 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
lcd_clrscr();
while (1)
{
code_rot = PIND & 0b00000011;
lcd_gotoxy(0,0);
lcd_aff_bin (code_rot, 2);
_delay_ms(10);
}
}
void lit_codeur_rot()
{
// codeur incrémental code Gray avec en plus fonction bouton poussoir
int p;
uint8_t fine = 0;
uint8_t mult = 10;
memo_code_rot = code_rot;
code_rot = PIND & 0b00000011;
fine = PIND & 0b00000100;
if (fine == 0b00000100) { mult = 10; } else { mult =1; } // reglage fin si bouton appuyé (pas = 0.1V) sinon pas = 1V
if (code_rot != memo_code_rot)
{
if ((memo_code_rot == 0b11) & (code_rot == 0b01)) { etat=1; p=0; }
if ((etat==1) & (code_rot == 0b00)) { etat=2; p=0;}
if ((etat==2) & (code_rot == 0b10)) { etat=3; p=0;}
if ((etat==3) & (code_rot == 0b11)) { etat=4; p=+1;}
if ((memo_code_rot == 0b11) & (code_rot == 0b10)) { etat=5; p=0; }
if ((etat==5) & (code_rot == 0b00)) { etat=6; p=0;}
if ((etat==6) & (code_rot == 0b01)) { etat=7; p=0;}
if ((etat==7) & (code_rot == 0b11)) { etat=8; p=-1;}
if ((etat==4) || (etat == 8))
{
consigne_V += (p * mult); p=0; etat =0;
if (consigne_V < 0) {consigne_V = 0;}
if (consigne_V > 210) {consigne_V = 0;}
if (consigne_V > 200) {consigne_V = 200;}
out_V();
}
lcd_gotoxy(12,0);
lcd_aff_nb (etat, 2, 0); // pour test du bouton encodeur
if (etat > 8) {etat =8; } // pour éviter de se retrouver dans une impasse...
_delay_ms(10);
}
}
/*
void lit_boutons_V(void)
{
// pour boutons poussoirs traditionnels
calcul_consigne_V();
memo_boutons_V = boutons_V;
boutons_V = PIND & 0b00001111;
if (boutons_V != memo_boutons_V)
{
//--------------------------------
// position du curseur (digit actif)
if( (boutons_V & bouton_L) == 0)
{
pos++;
if (pos>2) { pos=2;}
}
if( (boutons_V & bouton_R) == 0)
{
if (pos>0) { pos--;}
}
//--------------------------------
// edition du digit actif
//--------------- UP -----------------
if ( (boutons_V & bouton_U) == 0)
{
digits_consigne_V[pos]++;
if (digits_consigne_V[pos]>9)
{
digits_consigne_V[pos]=0;
digits_consigne_V[pos+1]++;
if (digits_consigne_V[pos+1]>9)
{
digits_consigne_V[pos+1]=0;
digits_consigne_V[pos+2]++;
}
}
calcul_consigne_V();
if (consigne_V > 200)
{
digits_consigne_V[0]=0;
digits_consigne_V[1]=0;
digits_consigne_V[2]=2;
calcul_consigne_V();
}
}
//--------------- DOWN -----------------
if (( (boutons_V & bouton_D) == 0) & (consigne_V > 0) )
{
if (digits_consigne_V[pos]>0)
{
digits_consigne_V[pos]--;
}
else if (digits_consigne_V[pos]==0)
{
if (digits_consigne_V[pos+1]>0)
{
digits_consigne_V[pos+1]--;
digits_consigne_V[pos]=9;
}
else if (digits_consigne_V[pos+2]>0)
{
digits_consigne_V[pos+2]--;
digits_consigne_V[pos+1]=9;
digits_consigne_V[pos]=9;
}
}
}
calcul_consigne_V();
if (consigne_V > 200) // possible ici par debordement lors de la decrementation
{
digits_consigne_V[0]=0;
digits_consigne_V[1]=0;
digits_consigne_V[2]=0;
}
calcul_consigne_V();
affiche_consigne_V();
}
while ( (PIND & 0b00001111) != 0b00001111) { ;} // boucle en attendant le relachement du bouton
}
*/
int main (void)
{
init_variables();
init_ports();
InitADC();
// InitINTs(); non utilisees
InitPWM();
//OCR1A = 65535;
lcd_init(LCD_DISP_ON_CURSOR);
lcd_clrscr();
lcd_home();
lcd_puts("ALIM 20V");
lcd_gotoxy(0,1);
lcd_puts("version ");
lcd_puts(version);
_delay_ms(1000);
lcd_clrscr();
consigne_V = 0;
affiche_consigne_V();
_delay_ms(500);
// ici la tension de sortie est maintenue = 0
// le courant est donc nul. Toutefois la mesure du courant n'est pas exactement nulle.
// on va donc l'annuler logiciellement (auto setup)
offset_I =0;
acqui_courant();
_delay_ms(200);
offset_I = mesure_I;
consigne_V = 50; // -> 5.0V
out_V();
// affiche_valeur_I(mesure_I);
// affiche_offset_I(offset_I);
//test_codeur_rot(); // pour test des états (diffèrent suivant modèles)
while(1)
{
lit_codeur_rot();
lit_boutons_I();
// acqui_tension();
compteur1++;
if (compteur1 >= 60000)
{
memo1_I = valeur_I;
acqui_courant();
if (offset_I > mesure_I) {valeur_I = offset_I - mesure_I;} else {valeur_I = 0;}
valeur_I *=15;
valeur_I += memo1_I;
valeur_I /=2;
affiche_valeur_I(valeur_I);
//affiche_offset_I(offset_I);
//lcd_gotoxy_clrEOL (0, 1);
//lcd_aff_nb (valeur16b, 5, 0);
compteur1=0;
}
_delay_us(10);
}
}