PS regulátor: Rozdiel medzi revíziami
Z SensorWiki
(Vytvorená stránka „<source lang="c"> #include <avr/io.h> #include <avr/interrupt.h> #include <stdlib.h> #include <stdio.h> #include "pomocne.h" FILE mystdout_uart = FDEV_SETUP_STREAM(s...“) |
|||
Riadok 93: | Riadok 93: | ||
#define SETBIT(VAR,BIT) ((VAR)|=(1<<(BIT))) | #define SETBIT(VAR,BIT) ((VAR)|=(1<<(BIT))) | ||
+ | |||
+ | </source> | ||
+ | |||
+ | A samozrejme aj samotné funkcie ('''pomocne.c'''): | ||
+ | <source lang="c"> | ||
+ | //#define epsilon_0 1 | ||
+ | #include <avr/io.h> | ||
+ | #include <avr/interrupt.h> | ||
+ | #include "pomocne.h" | ||
+ | |||
+ | volatile unsigned char t_out_spust_prev; | ||
+ | volatile unsigned char t_100ms; | ||
+ | volatile unsigned int iii=0; | ||
+ | volatile unsigned char vypis=0; | ||
+ | |||
+ | |||
+ | // Z PSD regulatora je implementovany len PS regulator | ||
+ | // Vynechana je cast Rucne/Automaticka prevadzka (len automaticka) | ||
+ | // Vynechany je ARW | ||
+ | // Vynechane je zadavanie parametrov | ||
+ | // Vynechany je watchdog,... | ||
+ | |||
+ | // Pri vypocte akcneho zasahu je pouzita celociselna aritmetika | ||
+ | // Parametre a vveliciny su normovane takto: | ||
+ | // Zosilnenie regulatora je vynasobene 10-timi, to znamena, ze Kreg=15 predsravuje zosilnenie 1.5 | ||
+ | // Integracna casova konstanta je vynasobena 1000. To znamena Ti_reg=500 predstavuje casovu konst. 0.5 sekundy | ||
+ | // Pozadovana hodnota w_zel je vynasobena 100-mi. To znamena w_zel1=450, predstavuje 4.5 V-ta. | ||
+ | // podobne to plati aj pre meranu velicinu. | ||
+ | // akcny zasah m_reg je z rozsahu 0 az % V. Tato velicina je prepocitana na Plnenie (8-bit) | ||
+ | |||
+ | int K_reg=10; | ||
+ | int Ti_reg=500; | ||
+ | volatile long m_P_n=0,m_P_s=0,m_I_s=0,m_I_n=0; | ||
+ | |||
+ | volatile int m_reg= 0; | ||
+ | int w_zel= 0; | ||
+ | int w_zel1=400,w_zel2=100; | ||
+ | volatile int reg_odch_n= 0; // velicina vypocitana v kroku (n*T_v) | ||
+ | volatile int reg_odch_s= 0; // velicina vypocitana v kroku ((n-1)*T_v) | ||
+ | |||
+ | volatile unsigned char plnenie=0; | ||
+ | volatile unsigned int y_reg; | ||
+ | int T_v= 19; // 110ms=109 20ms=19 | ||
+ | |||
+ | /* PS regulator */ | ||
+ | |||
+ | void PS_reg(void) | ||
+ | { | ||
+ | |||
+ | // vyposuvanie velicin | ||
+ | reg_odch_s = reg_odch_n; | ||
+ | m_I_s = m_I_n; | ||
+ | m_P_s = m_P_n; | ||
+ | |||
+ | // vypocet reg. odchylky | ||
+ | reg_odch_n = w_zel - y_reg; | ||
+ | |||
+ | // Algoritmus PS regulatora | ||
+ | // m(n*T_v)= m_P((x)*T_v)+ m_I((x)*T_v) | ||
+ | // x predstavuje krok (n*T_v) resp. ((n-1)*T_v) | ||
+ | // Vypocet P zlozky regulatora | ||
+ | m_P_n = reg_odch_n * K_reg; | ||
+ | if(Ti_reg) | ||
+ | { | ||
+ | // Vypocet I zlozky regulatora | ||
+ | |||
+ | m_I_n += (m_P_s*T_v)/Ti_reg; | ||
+ | // co by sa dialo ak by sme predchadzajuci riadok zapisali takto? | ||
+ | //m_I_n += (m_P_s/(Ti_reg))*T_v; | ||
+ | |||
+ | } | ||
+ | else | ||
+ | { // nechceme delit nulou | ||
+ | m_I_n = 0; | ||
+ | } | ||
+ | |||
+ | // epsilon vyjadruje posunutie IC zapojenie na vystupe regulatora | ||
+ | // epsilon je z intervalu (0, 1> | ||
+ | // tu uvazujeme lem krajne hodnoty | ||
+ | // epsilon = 0, znamena: po vypocitani sa zmeni hodnota akcneho zasahu okamzite | ||
+ | // epsilon = 1, znamena: hodnota akcneho zasahu sa zmeni az v dalsom kroku | ||
+ | |||
+ | #ifdef epsilon_0 | ||
+ | // vystup posunuty o krok | ||
+ | m_reg = (m_P_s + m_I_s)/10; | ||
+ | #else | ||
+ | // vystup neposunuty; vypocitam a poslem | ||
+ | m_reg = (m_P_n + m_I_n)/10; | ||
+ | #endif | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | // obmedzenie akcneho zasahu na interval <0 az 5 V> | ||
+ | if(m_reg>500) | ||
+ | { | ||
+ | m_reg=500; // 5.00 V | ||
+ | } | ||
+ | if(m_reg < 0) | ||
+ | { | ||
+ | m_reg=0; | ||
+ | } | ||
+ | // Doplnte vypocet, ako sme dostali cislo 2? | ||
+ | plnenie =(unsigned char) ((m_reg)/2); | ||
+ | OCR1A = plnenie; | ||
+ | |||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | // pouziva sa pri generovani timeout-ov | ||
+ | // pretecenie kazdu 1024 us = 1.0ms (preddelic nastaveny na fosc/64) | ||
+ | //#pragma interrupt_handler timer2_ovf_isr:iv_TIM2_OVF | ||
+ | ISR (TIMER2_OVF_vect) | ||
+ | //void timer2_ovf_isr(void) | ||
+ | { | ||
+ | TCNT2 = 0x83; //reload counter value | ||
+ | |||
+ | |||
+ | if (t_out_spust_prev) t_out_spust_prev--; | ||
+ | else | ||
+ | { t_out_spust_prev=T_v-1; // Tv = 110 ms = 109 | ||
+ | |||
+ | SETBIT(ADCSRA,ADSC);// spustenie dalsieho prevodu ADSC = TRUE | ||
+ | |||
+ | // rampa + meranie prechodovej charakteristiky | ||
+ | // if((iii++) <256) OCR1A=iii; else OCR1A=0x0; | ||
+ | |||
+ | } | ||
+ | |||
+ | // generator priebehu zelanej veliciny | ||
+ | if(t_100ms)t_100ms--; | ||
+ | else | ||
+ | {t_100ms = 99; // nastav pocitadlo 100 ms | ||
+ | if(iii++==50 )w_zel=w_zel1; | ||
+ | if(iii==100 )w_zel=w_zel2; | ||
+ | if(iii==150 )w_zel=w_zel1; | ||
+ | if(iii==200 )w_zel=w_zel2; | ||
+ | if(iii==250 )w_zel=w_zel1; | ||
+ | if(iii==300 )w_zel=w_zel2; | ||
+ | |||
+ | vypis=1; // kazdych 0.1 sekundy | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | // ************************************************************ | ||
+ | // prerusenie od A/D prevodnika | ||
+ | // ************************************************************ | ||
+ | ISR(ADC_vect) | ||
+ | { | ||
+ | long pom; | ||
+ | //conversion complete, read value (int) using... | ||
+ | // value=ADCL; //Read 8 low bits first (important) | ||
+ | // value|=(int)ADCH << 8; //read 2 high bits and shift into top byte | ||
+ | |||
+ | pom = ADC; | ||
+ | pom *= 500; | ||
+ | pom /= 1024; | ||
+ | y_reg = pom; // nanormovane na <0.00 az 5.00) | ||
+ | |||
+ | if (y_reg>500) | ||
+ | y_reg=500; | ||
+ | |||
+ | PS_reg(); | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | //TIMER2 initialize - prescale:128 | ||
+ | // WGM: Normal | ||
+ | // desired value: 1mSec | ||
+ | // actual value: 1,000mSec (0,0%) | ||
+ | void timer2_init(void) | ||
+ | { | ||
+ | TCCR2B = 0x00; //stop | ||
+ | ASSR = 0x00; //set async mode | ||
+ | TCNT2 = 0x83; //setup | ||
+ | OCR2A = 0x7D; | ||
+ | OCR2B = 0x00; | ||
+ | TCCR2A = 0x00; | ||
+ | TCCR2B = 0x05; //start | ||
+ | TIMSK2 = 0x01; // TOVIE2 = 1; Enable T2 overflow interrupt | ||
+ | } | ||
+ | |||
+ | //ADC initialize | ||
+ | // Conversion time: 104uS | ||
+ | void adc_init(void) | ||
+ | { | ||
+ | ADCSRA = 0x00; //disable adc | ||
+ | ADMUX = 0x44;//b0100 0100; //select adc input 4, Uref= 5,0V | ||
+ | //ADMUX b7 b6 b5 b4 b3 b2 b1 b0 | ||
+ | // ______________ | ||
+ | // MUX = 0b0100 => 4. kanal | ||
+ | // 0 | ||
+ | // ____ | ||
+ | // Right adjust = 0 | ||
+ | // ______ | ||
+ | // Refs = 01 => Uref = AVCC | ||
+ | ACSR = 0x80; // odpojenie Analog. komparator | ||
+ | |||
+ | // ADCSRB = 0x00; | ||
+ | ADCSRA = 0x8F; | ||
+ | //ADCSRA b7 b6 b5 b4 b3 b2 b1 b0 | ||
+ | // ___________ | ||
+ | // ADPS = 0b111 => Division Factor =128 => 16MHz/128 = 125 kHz | ||
+ | // ____ | ||
+ | // ADIE = 1 => povolenieprerusenia | ||
+ | // ____ | ||
+ | // ADIF = 1 => vynulovanie prerusenia | ||
+ | // 0 | ||
+ | // ____ | ||
+ | // ADSC = 1 => spustenie prevodu | ||
+ | // ____ | ||
+ | // ADEN = 01 => pripojenie AD na napatie | ||
+ | |||
+ | } | ||
+ | |||
+ | //TIMER1 initialize - prescale:64 | ||
+ | // WGM: 5) PWM 8bit fast, TOP=0x00FF | ||
+ | // desired value: 2mSec | ||
+ | // actual value: 1,024mSec (48,8%) | ||
+ | void timer1_init(void) | ||
+ | { | ||
+ | TCCR1B = 0x00; //stop | ||
+ | TCCR1A = 0x81; | ||
+ | TCCR1B = 0x0B; //start Timer | ||
+ | // Na pin PB1 je pripojeny OC1A FPWM mod | ||
+ | DDRB|=0x02; | ||
+ | } | ||
+ | |||
+ | // Funkcie pre obsluhu serioveho komunikacneho rozhrania: | ||
+ | |||
+ | void inituart(void) | ||
+ | { | ||
+ | BAUD_RATE_REG = (unsigned char)BAUD_PRESCALE; // Set baud rate: Load the UBRR register | ||
+ | |||
+ | UART_CONTROL_REG_B = (1 << ENABLE_RECEIVER_BIT)| | ||
+ | (1 << ENABLE_TRANSMITTER_BIT); // Enable receive + transmit | ||
+ | |||
+ | UART_CONTROL_REG_C = (3<<UCSZ00); // Added: Async. UART, None | ||
+ | // Parity, 8-data, 1 stopbit | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | int sendchar(char c, FILE *stream) | ||
+ | { | ||
+ | |||
+ | UART_DATA_REG = c; // prepare transmission | ||
+ | while (!(UART_STATUS_REG & (1 << TRANSMIT_COMPLETE_BIT)));// wait until byte sendt | ||
+ | UART_STATUS_REG |= (1 << TRANSMIT_COMPLETE_BIT); // delete TXCflag | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | int uart_putchar(char c, FILE *stream); | ||
+ | |||
+ | unsigned char recchar(void) | ||
+ | { | ||
+ | while( !(UART_STATUS_REG & (1 << RECEIVE_COMPLETE_BIT)) ); // wait for data | ||
+ | return UART_DATA_REG; | ||
+ | } | ||
+ | |||
+ | |||
</source> | </source> |
Verzia zo dňa a času 12:36, 17. december 2012
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <stdio.h>
#include "pomocne.h"
FILE mystdout_uart = FDEV_SETUP_STREAM(sendchar, NULL, _FDEV_SETUP_WRITE);
int main(void)
{
UCSR0B = 0x00; // Disable RxD and TxD for UART0
// Inicializacia
// Nie vsetky registre su rozpisane; doplnte
inituart(); // inicializacia UARTU
adc_init(); // inicializacia ADC
timer1_init(); // inicializacia Timer1: PWM
timer2_init(); // inicializacia Timer2: 1ms vzorky su zakladom
stdout = &mystdout_uart;
//printf("\x1B[1;1HAD [4]=");
sei(); // Enable ALL interrupts
do{
if(vypis){ // Vypis sa realizuje kazdych 100 ms
vypis=0;
//printf("\x1b[2;1H%04x %04d %04d %04d %3d.%02d ",plnenie,iii,w_zel,reg_odch_n,(y_reg/100),(y_reg%100));
printf("%d\r",y_reg/2);
}
} while(1);
}
K programu potrebujete aj knižnicu pomocne.h s pomocnými funkciami:
// Premenne:
extern int K_reg;
extern int Ti_reg;
extern volatile long m_P_s,m_P_n,m_I_s,m_I_n;
extern volatile int m_reg;
extern int w_zel;
extern int w_zel1,w_zel2;
extern volatile int reg_odch_n;
extern volatile int reg_odch_s;
volatile unsigned char plnenie;
extern volatile unsigned int y_reg;
extern int T_v; // 110ms=109 20ms=19
extern volatile unsigned char t_out_spust_prev;
extern volatile unsigned char t_100ms;
extern volatile unsigned int iii;
extern volatile unsigned char vypis;
// Hlavne funkcie:
extern void PS_reg(void);
extern void adc_init(void);
extern void timer1_init(void);
extern void timer2_init(void);
extern void timer2_ovf_isr(void);
// Pomocne funkcie:
extern void inituart(void);
extern int sendchar(char c, FILE *stream);
// Following calculation assumes that F_CPU is assigned in 'Project/Options'
#define BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (BAUDRATE * 16UL))) - 1)
/* definitions for UART control */
// Valid for ATmega328
#define BAUD_RATE_REG UBRR0 //
#define UART_STATUS_REG UCSR0A // OK, checked (but also control reg.)
#define UART_CONTROL_REG_B UCSR0B // OK, checked
#define UART_CONTROL_REG_C UCSR0C // Added for mode setting
#define ENABLE_TRANSMITTER_BIT TXEN0 // OK, checked
#define ENABLE_RECEIVER_BIT RXEN0 // OK, checked
#define DATA_REGISTER_EMPTY_BIT UDRE0 // Added, for possible speed-up
#define TRANSMIT_COMPLETE_BIT TXC0 // OK, checked
#define RECEIVE_COMPLETE_BIT RXC0 // OK, checked
#define UART_DATA_REG UDR0 // OK, checked
#define SETBIT(VAR,BIT) ((VAR)|=(1<<(BIT)))
A samozrejme aj samotné funkcie (pomocne.c):
//#define epsilon_0 1
#include <avr/io.h>
#include <avr/interrupt.h>
#include "pomocne.h"
volatile unsigned char t_out_spust_prev;
volatile unsigned char t_100ms;
volatile unsigned int iii=0;
volatile unsigned char vypis=0;
// Z PSD regulatora je implementovany len PS regulator
// Vynechana je cast Rucne/Automaticka prevadzka (len automaticka)
// Vynechany je ARW
// Vynechane je zadavanie parametrov
// Vynechany je watchdog,...
// Pri vypocte akcneho zasahu je pouzita celociselna aritmetika
// Parametre a vveliciny su normovane takto:
// Zosilnenie regulatora je vynasobene 10-timi, to znamena, ze Kreg=15 predsravuje zosilnenie 1.5
// Integracna casova konstanta je vynasobena 1000. To znamena Ti_reg=500 predstavuje casovu konst. 0.5 sekundy
// Pozadovana hodnota w_zel je vynasobena 100-mi. To znamena w_zel1=450, predstavuje 4.5 V-ta.
// podobne to plati aj pre meranu velicinu.
// akcny zasah m_reg je z rozsahu 0 az % V. Tato velicina je prepocitana na Plnenie (8-bit)
int K_reg=10;
int Ti_reg=500;
volatile long m_P_n=0,m_P_s=0,m_I_s=0,m_I_n=0;
volatile int m_reg= 0;
int w_zel= 0;
int w_zel1=400,w_zel2=100;
volatile int reg_odch_n= 0; // velicina vypocitana v kroku (n*T_v)
volatile int reg_odch_s= 0; // velicina vypocitana v kroku ((n-1)*T_v)
volatile unsigned char plnenie=0;
volatile unsigned int y_reg;
int T_v= 19; // 110ms=109 20ms=19
/* PS regulator */
void PS_reg(void)
{
// vyposuvanie velicin
reg_odch_s = reg_odch_n;
m_I_s = m_I_n;
m_P_s = m_P_n;
// vypocet reg. odchylky
reg_odch_n = w_zel - y_reg;
// Algoritmus PS regulatora
// m(n*T_v)= m_P((x)*T_v)+ m_I((x)*T_v)
// x predstavuje krok (n*T_v) resp. ((n-1)*T_v)
// Vypocet P zlozky regulatora
m_P_n = reg_odch_n * K_reg;
if(Ti_reg)
{
// Vypocet I zlozky regulatora
m_I_n += (m_P_s*T_v)/Ti_reg;
// co by sa dialo ak by sme predchadzajuci riadok zapisali takto?
//m_I_n += (m_P_s/(Ti_reg))*T_v;
}
else
{ // nechceme delit nulou
m_I_n = 0;
}
// epsilon vyjadruje posunutie IC zapojenie na vystupe regulatora
// epsilon je z intervalu (0, 1>
// tu uvazujeme lem krajne hodnoty
// epsilon = 0, znamena: po vypocitani sa zmeni hodnota akcneho zasahu okamzite
// epsilon = 1, znamena: hodnota akcneho zasahu sa zmeni az v dalsom kroku
#ifdef epsilon_0
// vystup posunuty o krok
m_reg = (m_P_s + m_I_s)/10;
#else
// vystup neposunuty; vypocitam a poslem
m_reg = (m_P_n + m_I_n)/10;
#endif
// obmedzenie akcneho zasahu na interval <0 az 5 V>
if(m_reg>500)
{
m_reg=500; // 5.00 V
}
if(m_reg < 0)
{
m_reg=0;
}
// Doplnte vypocet, ako sme dostali cislo 2?
plnenie =(unsigned char) ((m_reg)/2);
OCR1A = plnenie;
}
// pouziva sa pri generovani timeout-ov
// pretecenie kazdu 1024 us = 1.0ms (preddelic nastaveny na fosc/64)
//#pragma interrupt_handler timer2_ovf_isr:iv_TIM2_OVF
ISR (TIMER2_OVF_vect)
//void timer2_ovf_isr(void)
{
TCNT2 = 0x83; //reload counter value
if (t_out_spust_prev) t_out_spust_prev--;
else
{ t_out_spust_prev=T_v-1; // Tv = 110 ms = 109
SETBIT(ADCSRA,ADSC);// spustenie dalsieho prevodu ADSC = TRUE
// rampa + meranie prechodovej charakteristiky
// if((iii++) <256) OCR1A=iii; else OCR1A=0x0;
}
// generator priebehu zelanej veliciny
if(t_100ms)t_100ms--;
else
{t_100ms = 99; // nastav pocitadlo 100 ms
if(iii++==50 )w_zel=w_zel1;
if(iii==100 )w_zel=w_zel2;
if(iii==150 )w_zel=w_zel1;
if(iii==200 )w_zel=w_zel2;
if(iii==250 )w_zel=w_zel1;
if(iii==300 )w_zel=w_zel2;
vypis=1; // kazdych 0.1 sekundy
}
}
// ************************************************************
// prerusenie od A/D prevodnika
// ************************************************************
ISR(ADC_vect)
{
long pom;
//conversion complete, read value (int) using...
// value=ADCL; //Read 8 low bits first (important)
// value|=(int)ADCH << 8; //read 2 high bits and shift into top byte
pom = ADC;
pom *= 500;
pom /= 1024;
y_reg = pom; // nanormovane na <0.00 az 5.00)
if (y_reg>500)
y_reg=500;
PS_reg();
}
//TIMER2 initialize - prescale:128
// WGM: Normal
// desired value: 1mSec
// actual value: 1,000mSec (0,0%)
void timer2_init(void)
{
TCCR2B = 0x00; //stop
ASSR = 0x00; //set async mode
TCNT2 = 0x83; //setup
OCR2A = 0x7D;
OCR2B = 0x00;
TCCR2A = 0x00;
TCCR2B = 0x05; //start
TIMSK2 = 0x01; // TOVIE2 = 1; Enable T2 overflow interrupt
}
//ADC initialize
// Conversion time: 104uS
void adc_init(void)
{
ADCSRA = 0x00; //disable adc
ADMUX = 0x44;//b0100 0100; //select adc input 4, Uref= 5,0V
//ADMUX b7 b6 b5 b4 b3 b2 b1 b0
// ______________
// MUX = 0b0100 => 4. kanal
// 0
// ____
// Right adjust = 0
// ______
// Refs = 01 => Uref = AVCC
ACSR = 0x80; // odpojenie Analog. komparator
// ADCSRB = 0x00;
ADCSRA = 0x8F;
//ADCSRA b7 b6 b5 b4 b3 b2 b1 b0
// ___________
// ADPS = 0b111 => Division Factor =128 => 16MHz/128 = 125 kHz
// ____
// ADIE = 1 => povolenieprerusenia
// ____
// ADIF = 1 => vynulovanie prerusenia
// 0
// ____
// ADSC = 1 => spustenie prevodu
// ____
// ADEN = 01 => pripojenie AD na napatie
}
//TIMER1 initialize - prescale:64
// WGM: 5) PWM 8bit fast, TOP=0x00FF
// desired value: 2mSec
// actual value: 1,024mSec (48,8%)
void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCCR1A = 0x81;
TCCR1B = 0x0B; //start Timer
// Na pin PB1 je pripojeny OC1A FPWM mod
DDRB|=0x02;
}
// Funkcie pre obsluhu serioveho komunikacneho rozhrania:
void inituart(void)
{
BAUD_RATE_REG = (unsigned char)BAUD_PRESCALE; // Set baud rate: Load the UBRR register
UART_CONTROL_REG_B = (1 << ENABLE_RECEIVER_BIT)|
(1 << ENABLE_TRANSMITTER_BIT); // Enable receive + transmit
UART_CONTROL_REG_C = (3<<UCSZ00); // Added: Async. UART, None
// Parity, 8-data, 1 stopbit
}
int sendchar(char c, FILE *stream)
{
UART_DATA_REG = c; // prepare transmission
while (!(UART_STATUS_REG & (1 << TRANSMIT_COMPLETE_BIT)));// wait until byte sendt
UART_STATUS_REG |= (1 << TRANSMIT_COMPLETE_BIT); // delete TXCflag
return 0;
}
int uart_putchar(char c, FILE *stream);
unsigned char recchar(void)
{
while( !(UART_STATUS_REG & (1 << RECEIVE_COMPLETE_BIT)) ); // wait for data
return UART_DATA_REG;
}