Sida 1 av 2
Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 13:09:49
av libbresse
Idag tar det för länge att skriva ut varvtalet, finns det något snabbare sätt än det jag har implementerat nedan:
Kod: Markera allt
varvtal
for(int i; i < varvtal;i+100)
{
g_hundra++;
if (g_hundra>9)
{
g_tusen++;
g_hundra=0;
}
}
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 13:18:40
av Micke_s
dela med 10 varje gång och ta modulo
edit eller tvärtom ta modulo 10 först och sedan delar du det
siffra1000 = tal % 10
tal /= 10;
siffra100 = tal % 10
tal /= 10;
siffra10 = tal % 10
tal /= 10;
siffra1 = tal % 10
Antar att det du är ute efter.
Fast vad som går fortast beror på om du har hårdvarumultiplicering / dividering mm
Edit: AVR har bara hårdvarumultiplicering, ingen dividering
Edit3:
kanske smarare att göra såhär
siffra1000 = 0;
siffra100 = 0;
siffra10 = 0;
siffra1 = 0;
while(intal > 1000){
siffra1000++;
intal-=1000;
}
while(intal > 100){
siffra100++;
intal-=100;
}
while(intal > 10){
siffra10++;
intal-=10;
}
siffra1 = intal
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 14:46:26
av bos
Snälla, använd code-taggar när ni postar programkod.
Sen, vad är "snabbt"? Vi vet inte hur många cykler OP-koden tar, blir svårt att optimera ner det då.
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 15:33:04
av libbresse
Det var tydligen inte det som var felet, testade Micke_s kod och fick samma fel. vid högre frekvenser 150HZ
Tar in både timerinterupt och pinchange interrupt. Kan det hända att dom krockar med varandra om dessa händer samtidigt
hur tar jag reda på hur många cykler OP-koden tar ?
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 15:37:18
av sodjan
> hur tar jag reda på hur många cykler OP-koden tar
Antingen har du något verktyg som kan räkna cykler. T.ex en simulator
med stoppursfunktion.
Eller så tar du assembler-listan från C-kompileringen och räknar "för hand"...
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 15:46:57
av Icecap
libbresse: får jag bara fråga... gör du denna omvandling i ISR'n?
För OM du gör det är det alldeles fel! Man lägger sådana funktioner i Main-loop'en och signalerar från ISR'n att den ska utföras.
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 16:44:14
av libbresse
Sodjan: pust det verkar jobbigt
Icecap:
Kod: Markera allt
signal(sig_pin_change1)
{
cli();
new_event=1;
sei();
}
signal(sig_output_compare1a)
{
Cli();
irpmCount++;
irpmCount2++;
sei();
}
och sedan kod i main som tar hand om variablerna. Borde väll fungera

Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 17:05:03
av sodjan
En detalj bara...
Måste du hantera cli() och sei() så att säga "för hand" i koden ?
Hanteras inte det av interruptet i sig och av "return from interrupt" ?
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 17:18:27
av Icecap
Håller med sodjan här, jag har aldrig sett en µC där man måste gör på det vis.
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 22 oktober 2009, 20:27:07
av Micke_s
Kom ihåg att variablerna måste vara volatile för inte optimeras bort om den används både i main och isr.
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 23 oktober 2009, 01:42:13
av hatten
När en interrupt hanteras så nollas I-biten automagiskt, precis så som sodjan misstänker. Efter att ISRen har körts, dvs då RETI (Return from Interrupt) exekveras så sätts I-biten igen. Man kan, däremot, smyga in och sätta I-biten i början av en ISR och på så sätt få nästlade interrupts. Jag har för mig att interrupterna prioriteras efter sin respektive vektors adress.
Vad gäller ursprungsfrågan så hänger jag inte riktigt med, men av Micke_s kod att döma så gissar jag att du kan använda dig av funktionen itoa(int val, char* s, int radix) i
stdlib.
Klistra gärna in hela koden.
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 23 oktober 2009, 08:50:28
av bos
itoa() lär ju vara segare än sirap i en påse med klister. Det OP eftersöker är en kod som omvandlar en variabel till ental, tiotal osv så att de kan skrivas ut tecken för tecken på en LCD. Generellt sett är while-koden som Micke visade den snabbaste, för då slipper man använda division och modulo.
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 23 oktober 2009, 12:40:17
av libbresse
Aha,,,, jag får testa ta bort cli och sei då. Jag klistrar in koden nedan. Av någon anledning fick jag det inte att fungera utan _delay_loop_2() på vissa ställen ????
Kod: Markera allt
// Blinky.c
#include <inttypes.h>
#include <avr/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include "LCD.h"
#include <avr/signal.h>
#define pinb_mask ((1<<PINB7))
// för att identifiera knapparna
#define menu 5
#define go 6
#define stop 7
#define plus 8
#define minus 9
#define badcommand 17
#define sens_int 18 // sensorinterrupt
#define on 15
#define off 16
int irpmShow; // uppdateringsfrekvens
int iTriggerCount;
int timer;
int timer2;
int irpmCount;
int irpmCount2;
int irpmCount_old;
int irpmCalc;
char new_event;
int irpmnumb;
char g_tusental ;
char g_hundratal;
char g_tiotal;
void uppdateLcd(void);
void init(void);
void OSCCAL_calibration(void);
void twelveOn(int p);
void rpmShow(void);
void RTC_init(void);
int main (void)
{
init();
while(1)
{
if((irpmCount>21)&(timer == 1))
{
timer=0;
twelveOn(off);
}
else
{
}
if(new_event==1) // gå in här om vi har fått in ett interrupt
{
if(iTriggerCount >= 5) // sätter på spänning var 3dje puls
{
irpmCount =0;
twelveOn(on); // 12 volt
rpmShow(); // program som skriver in varvtalet
iTriggerCount = 0;
timer=1;
new_event =0; // interruptet är klart
}
else
{
iTriggerCount++;
new_event =0;
}
}
else
{
_delay_loop_2(1);
}
}
return 1;
}
SIGNAL(SIG_PIN_CHANGE1)
{
cli();
new_event =1;
sei();
}
void twelveOn(int p) // 12 volt
{
if(p==on)
{
PORTB = ((PORTB)|(1<<PINB4));//kör bara ut signalen till motorn på PINB6
_delay_loop_2(1);
}
else
{
PORTB = ((PORTB)&~(1<<PINB4));//kör bara ut signalen till motorn på PINB6
_delay_loop_2(1);
}
}
void rpmShow(void) // skriver ut varvtalet
{
if (irpmShow > 50)
{
if(irpmnumb >= 1) // räknar medelvärdet på flera varv 0 = 1 varv 1=2 osv
{
g_tiotal=0;
g_hundratal = 0;
g_tusental =0;
irpmCalc =(116000)/irpmCount2; // finns det något smartare sätt för denna ???
/**
*for(int i=0;i < irpmCalc;i=i+100) // den här tar för länge
*{
* g_hundratal++;
* if(g_hundratal>9)
* {
* g_hundratal = 0;
* g_tusental++;
*
* }
*
*}
*/
irpmCalc /=10;
g_tiotal = irpmCalc%10;
irpmCalc /=10;
g_hundratal=irpmCalc%10;
irpmCalc/=10;
g_tusental=irpmCalc%10;
uppdateLcd();
irpmShow=0;
irpmnumb =0;
irpmCount2 =0;
}
else
{
irpmnumb++;
_delay_loop_2(1);
}
}
else
{
irpmShow++;
_delay_loop_2(1);
irpmCount2 =0;
}
}
/* ISR som skall exekvera var xxxmillisekund*/
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
cli();
irpmCount++;
irpmCount2++;
sei();
}
void init(void)
{
irpmnumb =0;
new_event =0;
irpmShow=0;
// set PORTD for output
DDRD = (0xFF);
//set portb for input
DDRB = ((1<<PINB4)|(1<<PINB5));
// pullup on the pinns we will use
PORTB |= ((pinb_mask));
//enable pin change interrupt on PORTB
PCMSK1 = pinb_mask|(1<<PINB7);
EIFR = (1<<6)|(1<<7);
EIMSK = (1<<6)|(1<<7);
DDRB = ((1<<PINB4)|(1<<PINB5));
PORTB=(0xFF&~(1<<PINB4)&~(1<<PINB5)&~(1<<PINB7));
OSCCAL_calibration(); // set internal clock to 8 MHZ
RTC_init();
iTriggerCount=0;
// initialize the LCD
LCD_Init();
g_hundratal = 7;
g_tusental =7 ;
uppdateLcd();
}
void uppdateLcd(void)
{
LCD_putc(0,g_tusental +'0'); /* Skriv till LDC-minnet på sifferplats nr 0 */
/* variabeln g_sekund + asciikoden för noll blir motsvarande sekund i asciikod */
LCD_putc(1,g_hundratal + '0');
/* variabeln g_tiondel + asciikoden för noll blir motsvarande tiondel i asciikod */
LCD_putc(2,g_tiotal+'0');
LCD_putc(3,'0');
/* Updatera LCD */
LCD_UpdateRequired();
}
//32,768 kHz crystal as reference
//Behöver ej kommenteras
void OSCCAL_calibration(void)
{
unsigned char calibrate = 0;//FALSE;
int temp;
unsigned char tempL;
//CLKPR = (1<<CLKPCE); // set Clock Prescaler Change Enable obs bortkommenterad
// set prescaler = 1, Inter RC 8Mhz / 1 = 8Mhz
//CLKPR = (1<<CLKPS1) | (1<<CLKPS0); bortkommenterad för att komma till 8 mhz
TIMSK2 = 0; //disable OCIE2A and TOIE2
ASSR = (1<<AS2); //select asynchronous operation of timer2 (32,768kHz)
OCR2A = 100; // set timer2 compare value obs ändrad för att komma till 8 mhz
TIMSK0 = 0; // delete any interrupt sources
TCCR1B = (1<<CS10); // start timer1 with no prescaling
TCCR2A = (1<<CS20); // start timer2 with no prescaling
while((ASSR & 0x01) | (ASSR & 0x04)); //wait for TCN2UB and TCR2UB to be cleared
// wait for external crystal to stabilise
for(int i = 0; i < 10; i++)
_delay_loop_2(30000);
while(!calibrate)
{
cli(); // mt __disable_interrupt(); // disable global interrupt
TIFR1 = 0xFF; // delete TIFR1 flags
TIFR2 = 0xFF; // delete TIFR2 flags
TCNT1H = 0; // clear timer1 counter
TCNT1L = 0;
TCNT2 = 0; // clear timer2 counter
while ( !(TIFR2 && (1<<OCF2A)) ); // wait for timer2 compareflag
TCCR1B = 0; // stop timer1
sei(); // __enable_interrupt(); // enable global interrupt
if ( (TIFR1 && (1<<TOV1)) )
{
temp = 0xFFFF; // if timer1 overflows, set the temp to 0xFFFF
}
else
{ // read out the timer1 counter value
tempL = TCNT1L;
temp = TCNT1H;
temp = (temp << 8);
temp += tempL;
}
if (temp > 6250)
{
OSCCAL--; // the internRC oscillator runs to fast, decrease the OSCCAL
}
else if (temp < 6120)
{
OSCCAL++; // the internRC oscillator runs to slow, increase the OSCCAL
}
else
calibrate = 1;//TRUE; // the interRC is correct
TCCR1B = (1<<CS10); // start timer1
}
}
void RTC_init(void)
{
cli();
TCCR1A = 0; //
TCCR1B = (1<<WGM12)|(1<<CS10); // kör på 8 bits prescaling @ 8 mhz
OCR1A = 100; // ett timerinterrupt varje xxx
TIMSK1 = (1<<OCIE1A); //lokalt interrupt
// Global interrupt enable
sei();
}
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 23 oktober 2009, 12:56:30
av libbresse
volatile .......... vad är det ?
Re: Hur skriva varvtal snabbt till LCD AVR BUTTERFLY
Postat: 23 oktober 2009, 13:58:29
av Icecap
"volatile" är en flagga till kompilern som beskriver att variabeln kan ändras av t.ex interrupt eller att det är en portpinne, kompilern kan i vilket fall inte anta att den är stabil.
Tänk att den kunde komma in en loop vid att vara '1' och den loop väntar på att den blir '0'. Om kompilern får optimera hårt anser den att "den är '1' när vi kommer in i loopen och det är den sannolikt härefter" varför programmet låser sig. Med "volatile" anger man då att den _ska_ kolla variabeln om man skriver det.
Och ja, exemplet är reellt, jag har haft just det problem i sin tid, att deklarera variabeln som "volatile" löste detta.
Så alla variabler som t.ex. används i main-loop men som kan ändras av en ISR måste(/bör) vara deklarerat som "volatile". I många fall gör kompilern rätt men när det skiter sig är det omöjligt att hitta felet då man ju faktisk har skrivit rätt men kompilern har optimerat bort det...