Portál AbcLinuxu, 4. listopadu 2025 17:53
ISR(TIMER0_OVF_vect) {
TCNT0=6;
longac++;
if (longac>230) {
display_voltage();
longac=0;
}
if (engine_active) {
if (OCR1A <= motor_stop && E_llllNEW) {
E_llll = E_llllNEW;
E_llllNEW = 0;
}
if (E_a) {
if (! OCR1A) {
bitClear (PORTC, PC1);
bitSet (PORTC, PC0);
}
} else {
if (! OCR1A) {
bitClear (PORTC, PC0);
bitSet (PORTC, PC1);
}
}
if (OCR1A < E_llll) {
OCR1A++;
}
if (OCR1B < E_pppp) {
OCR1B++;
}
if (OCR1A > E_llll) {
OCR1A--;
}
if (OCR1B > E_pppp) {
OCR1B--;
}
}
if ((OCR1A+OCR1B)==0) engine_active=0;
}
ISR(USART_RXC_vect) {
unsigned char status,data,i;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | DATA_OVERRUN))==0) {
if((data=='\n')) { rs232enter=1; }
if(data>=32 && data<=126) {
i=RX_BUFFER_SIZE-1;
while(i>0) {
rx_buffer[i]=rx_buffer[i-1];
i--;
}
rx_buffer[0]=data;
}
}
}
voltage=analog2v(convertanalog(0));
write(PSTR("Akumulátor: "));
writestr(voltage2float(voltage));
void putchar1(char c) {
while (!(UCSRA & DATA_REGISTER_EMPTY));
UDR=c;
}
void write(char *sss){
char k;
while ((k=pgm_read_byte(sss++))) {
putchar1(k);
}
}
void writestr(char *sss){
char k;
while ((k=(*sss++))) {
putchar1(k);
}
}Funkce pro zjištění napětí jsem opráskl odtud: http://tuxgraphics.org/common/src2/article07061/
analog.c
/* vim: set sw=8 ts=8 si et: */
/*
* ADC functions for atmega8.
* Author: Guido Socher, Copyright: GPL
* http://tuxgraphics.org/electronics/
*/
#include <avr/io.h>
//----------------EDIT HERE----------------------------------------
// VDIV = (Rx+Ry)/Rx, change this according to the division factor of Rx and Ry
//
// This is if you do not use any resistor for Rx and Ry=4K7 (measure up to 2.5V):
//#define VDIV 1.0
//
// Ry=4K7 Rx=4K7 -> divide by 2 (measure up to 5V)
#define VDIV 7.745
// convert adc reading to voltage
unsigned int analog2v(unsigned int aval)
{
float r;
// 100* 2.56*VDIV/1024:
r=(aval * VDIV )/4.0;
return((unsigned int)(r+0.5));
}
//-------------END EDIT HERE----------------------------------------
// return analog value of a given channel. Works without interrupt
unsigned int convertanalog(unsigned char channel)
{
unsigned char adlow,adhigh;
/* enable analog to digital conversion in single run mode
* without noise canceler function. See datasheet of atmega88 page 250
* We set ADPS2=1 ADPS0=1 ADPS1=1 to have a clock division factor of 128.
* This is needed to stay in the recommended range of 50-200kHz
* Clock freq= 14MHz or 18 MHz
* ADEN: Analog Digital Converter Enable
* ADIE: ADC Interrupt Enable (0=no interrupt)
* ADIF: ADC Interrupt Flag
* ADCSR: ADC Control and Status Register
* ADPS2..ADPS0: ADC Prescaler Select Bits
* REFS: Reference Selection Bits (page 203)
*/
// int-ref with external capacitor at AREF pin:
// atmega8: 2.56V int ref=REFS1=1,REFS0=1
// atmega88: 1.1V int ref=REFS1=1,REFS0=1
// write only the lower 3 bit for channel selection
//
#ifdef USEAVCCREF
// AVcc ref
ADMUX=(1<<REFS0)|(channel & 0x0f);
#else
// Use the intenal ref:
ADMUX=(1<<REFS1)|(1<<REFS0)|(channel & 0x0f);
#endif
//
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
// switch off digital input line:
//DIDR0=(1<<channel)& 0x1f;
// start conversion
ADCSRA|= (1<<ADSC);
while(bit_is_set(ADCSRA,ADSC)); // wait for result
adlow=ADCL; // read low first !!
adhigh=ADCH;
return((unsigned int)((adhigh<<8)|(adlow & 0xFF)));
}
analog.h
/* vim: set sw=8 ts=8 si et : */
/*
* Title : C include file for analog conversion
* Copyright: GPL V2
* Autor: Guido Socher
* http://tuxgraphics.org/electronics/
*/
#ifndef ANALOG_H
#define ANALOG_H
// return analog value of a given channel.
extern unsigned int convertanalog(unsigned char channel);
extern unsigned int analog2v(unsigned int aval);
#endif /* ANALOG_H */
voltage=analog2v(convertanalog(0));
write(PSTR("Akumulátor: "));
writestr(voltage2float(voltage));
Tohle je kód te funkce display_voltage(), kterou voláš v přerušení při přetečení časovače? Jestli jo, tak je problém jasný - při běhu obsluhy přerušení je zakázaná obsluha jiných přerušení, takže ISR(USART_RXC_vect)() se neprovede včas a UDR přeteče.
Je potřeba zbavit se kódu, který může běžet dlouho, a vykonává se se zakázaným přerušením. Možnosti:
1) Nevím, jak vypadá tvoje funkce main(), ale jestli je tam jenom nekonečná prázdná smyčka, tak můžeš definovat globální proměnnou, která bude do hlavní smyčky signalizovat, že se má provést měření, a odvysílat výsledky. Např.:
// globální proměnná je definovaná někde na začátku
volatile unsigned char signal_pro_mereni; //musí být volatile
void main () {
...
signal_pro_mereni = 0; // to přijde někam na začátek
...
...
while (1) {
if (signal_pro_mereni) { // do prázdné smyčky se přidá měření
signal_pro_mereni = 0;
display_voltage();
}
}
}
ISR(TIMER0_OVF_vect) {
TCNT0=6;
longac++;
if (longac>230) {
signal_pro_mereni = 1;
longac=0;
...
}
Rozdíl je v tom, že v tomto případě se měření provede s povoleným přerušením, takže když přijdou data ze sériového portu, tak se přijmou. (Samozřejmě musíš zajistit, aby přijetí dat a zpracování příkazů nezpůsobilo nějakou kolizi v odvysílání.)
2) Na čas nejnáročnější je samotné vysílání po sériovém portu, možná bude stačit ho spouštět v přerušení. Např.:
#define BUFFER_LEN 128
unsigned char outbuffer[BUFFER_LEN]; // buffer pro odesílání, velikost upravit podle potřeby
unsigned char out_len; // délka dat
unsigned char index; // odtud se bude vysílat
volatile unsigned char vysila_se;
void write (char *sss) {
unsigned char k;
if (vysila_se) return;
if (out_len == BUFFER_LEN) return;
while ((k = pgm_read_byte(sss++))) {
outbuffer[out_len++] = k;
if (out_len == BUFFER_LEN) break;
}
}
void writestr(char *sss){
char k;
if (vysila_se) return;
if (out_len == BUFFER_LEN) return;
while ((k=(*sss++))) {
outbuffer[out_len++] = k;
if (out_len == BUFFER_LEN) break;
}
}
void odvysilat() {
if (vysila_se) return;
if (!out_len) return;
if (out_len == 1) {
UDR = outbuffer[0];
out_len = 0;
return;
}
vysila_se = 1;
out_index = 0;
UDR = outbuffer[out_index++];
UCSRB |= (1 << UDRIE);
}
ISR (USART_UDRE_vect) {
UDR = outbuffer[out_index++];
if (out_index == out_len) {
UCSRB &= ~(1 << UDRIE);
vysila_se = 0;
out_len = 0;
}
}
Funkce write() a writestr() naplní vysílací buffer a zavolání odvysilat() ho odvysílá po sériovém portu. Program běží dál a jenom se občas přeruší, když je potřeba zapsat další znak do UDR.
(Tenhle konkrétní kód jsem netestoval, ale obvykle to dělám podobně a funguje mi to, takže princip je v podstatě v pořádku.)
3) V putchar() periodicky testovat UCSRA & (1 << RXC) a když se zjistí přijatý znak, tak zavolat nějakou funkci, která bude duplikovat kód obluhy přerušení USART_RXC_vect, ale to mi přijde dost ošklivé.
P.S.: Jestli ty tři řádky na začátku nejsou v display_voltage(), tak sem hoď ještě display_voltage().
while (1) {
if (je co dekódovat) { // předpokládám, že to tam je nějak takhle
dekódovat;
}
if (signal_pro_mereni) {
signal_pro_mereni = 0;
display_voltage();
}
}
Nevýhoda je, že vysílání ti zablokuje dekódování těch příkazů a ty se dokódují, až když se dovysílá. Pokud to nepůjde ani takhle, tak ti zbývá jenom možnost 2 nebo 3.
ISR(XXX_vect, ISR_NOBLOCK)
{
...
}
Tak bych nadefinoval TIMER0_OVF_vect, s tím, že při vstupu do přerušovací rutiny stopnu časovač TIMER0 a na konci rutiny ho zase spustím. Nevýhoda je v tom, že by měření neprobíhalo v periodických intervalech, intervaly mezi měřeními by se měnily podle toho kolik času by ATmega strávila vysíláním dat.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.