/*==============================================================================
par Silicium628
derniere mise à jour 18 juillet 2013
================================================================================
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 = "4.2";
/*
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;
uint8_t digits_consigne_V[3]; // 0..199 pour 0V.. a 19.9V
uint16_t I_max;
uint16_t mesure_V;
uint16_t memo1_V;
uint16_t memo2_V;
uint16_t memo3_V;
uint16_t memo4_V;
uint16_t mesure_I;
uint8_t pos;
uint8_t pos_curseur;
uint8_t boutons_I;
uint8_t boutons_V;
uint8_t memo_boutons_V;
uint16_t compteur1;
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
TCCR1A |= (1 << WGM13) | (1 << WGM12) | (1 << WGM11) | (1 << WGM10); // set 16bit phase corrected PWM Mode
// TCCR1B |= (1 << CS11); // set prescaler to 8 and starts PWM
TCCR1B |= (1 << CS10); // set no prescaler and starts PWM
OCR1A = 65535;
}
void init_variables(void)
{
// OCR2=255;
digits_consigne_V[0] = 0;
digits_consigne_V[1] = 5; // 5V
digits_consigne_V[2] = 0;
mesure_V=0;
memo1_V=0;
memo2_V=0;
memo3_V=0;
memo4_V=0;
}
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_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_mesure_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_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 calcul_consigne_V()
{
consigne_V=digits_consigne_V[0]+10*digits_consigne_V[1]+100*digits_consigne_V[2];
}
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
}
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 lit_boutons_V(void)
{
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
}
void test (void)
{
lcd_clrscr();
lcd_puts("TEST");
}
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("ALIMENTATION 20V");
lcd_gotoxy(0,1);
lcd_puts("version ");
lcd_puts(version);
_delay_ms(2000);
lcd_clrscr();
calcul_consigne_V();
affiche_consigne_V();
uint16_t q1;
while(1)
{
lit_boutons_V();
lit_boutons_I();
acqui_tension();
acqui_courant();
//ici il faut comparer [mesure_V] a [consigne_V]
//consigne_V = 0..200
//mesure_V = 0..1000 (par ajustement du pont diviseur resistif externe)
q1= (uint16_t) (mesure_V / 5);
//q1 = 0..200
// asservissement du rapport cyclique du signal OCR1A et donc de la tension
if (mesure_I < I_max) // cas ou il n'y a pas de dépassement du courant max admissible:
{
if ((q1 < consigne_V) & (OCR1A>0)) { OCR1A -= 1; }
if ((q1 > consigne_V) & (OCR1A<65535)) { OCR1A += 1; }
_delay_ms(10);
}
else // limitation en courant
{
if (OCR1A<65535) {OCR1A += 1;} // fait chuter l'angle de conduction
if((mesure_I - I_max) < 500) { _delay_ms(5); } // très vite pour les grands dépassements puis
// plus lentement pour les 500 derniers mA sinon la tension devient fluctuante.
}
compteur1++;
if (compteur1>=100)
{
affiche_mesure_I(mesure_I);
affiche_consigne_I();
compteur1=0;
}
}
}