Nybörjarfrågor Atmega168 och GPS-modul
- jadler
- EF Sponsor
- Inlägg: 407
- Blev medlem: 28 maj 2009, 12:03:43
- Ort: Vidja, Huddinge, Stockholm
- Kontakt:
Re: Nybörjarfrågor Atmega168 och GPS-modul
Tips: Du vill antagligen köra signalerna från GPS:en genom en inverterare (74HC04 eller liknande) om du inte löser det i din UART-kod istället. Jag antar att din Globalsat-modul liksom min kommunicerar på TTL-nivå men med logiska nivåer som RS232, inverterat jämfört med "vanlig" TTL-UART. Jag hade stora problem att få det att fungera innan jag insåg det.
Just tolkning och hantering av NMEA ligger på en högre nivå och är mer generell, så kanske du kan ha glädje av att kika på några av alla de program och bibliotek som finns skrivna för Arduino (samma AVR-familj) just för att tolka NMEA-strängar.
Just tolkning och hantering av NMEA ligger på en högre nivå och är mer generell, så kanske du kan ha glädje av att kika på några av alla de program och bibliotek som finns skrivna för Arduino (samma AVR-familj) just för att tolka NMEA-strängar.
Re: Nybörjarfrågor Atmega168 och GPS-modul
om den kör inverterat är det nog inget processorn klarar av (inte med hårdvaru-uart i alla fall), så det blir nog att skaffa invterterare (eller transistorer och ett par motstånd). Det är ju lätt att kolla med en voltmeter. Om den inte sänder data (eller bara sänder lite ibland) så ligger spänningen på runt 0v om den är inverterad, annars ligger den runt +5v.
Re: Nybörjarfrågor Atmega168 och GPS-modul
Jag har för mig att nyare PIC:ar kan invertera UART-datan i hårdvaran. Men, nej det är nog inte så vanligt.
Re: Nybörjarfrågor Atmega168 och GPS-modul
Jag läste det som att "är det nog inget processorer klarar av". Mer som en allmän sak. Men jag ser nu att du syftade specifikt på just den processorn. Då blev ju mitt inlägg lite provocerande... 

- jadler
- EF Sponsor
- Inlägg: 407
- Blev medlem: 28 maj 2009, 12:03:43
- Ort: Vidja, Huddinge, Stockholm
- Kontakt:
Re: Nybörjarfrågor Atmega168 och GPS-modul
Nu vet jag inte om Glattnos redan har löst allt, men jag tänkte bidra med lite länkar och tankar.
Min beskrivning av problemet med inverterade seriesignaler och hur jag löste det.
Vad gäller kod för seriell kommunikation med AVR kan jag dels rekommendera användarhandboken för avr-libc (deras FAQ lär i princip vara obligatorisk läsning för den som vill programmera AVR med avr-gcc), dels Atmels Application Notes for AVR där man bl.a. hittar olika exempel på hur man kan lösa seriell kommunikation med AVR (t.ex. AVR306: Using the AVR UART in C). Avr-libc-handboken har flera programexempel, där ett implementerar UART, och exemplen är väl dokumenterade. Själva libc-avr innehåller bland mycket annat ett bibliotek för att förenkla uträkning av relevanta konstanter för olika bps (felaktigt kallat baudrate i nästan alla sammanhang idag).
NMEA-tolkning:
http://www.maartenlamers.com/nmea/
http://arduiniana.org/libraries/TinyGPS/
http://www.arduino.cc/playground/Tutorials/GPS
http://www.arduino.cc/cgi-bin/yabb2/YaB ... 166042147/
Min beskrivning av problemet med inverterade seriesignaler och hur jag löste det.
Vad gäller kod för seriell kommunikation med AVR kan jag dels rekommendera användarhandboken för avr-libc (deras FAQ lär i princip vara obligatorisk läsning för den som vill programmera AVR med avr-gcc), dels Atmels Application Notes for AVR där man bl.a. hittar olika exempel på hur man kan lösa seriell kommunikation med AVR (t.ex. AVR306: Using the AVR UART in C). Avr-libc-handboken har flera programexempel, där ett implementerar UART, och exemplen är väl dokumenterade. Själva libc-avr innehåller bland mycket annat ett bibliotek för att förenkla uträkning av relevanta konstanter för olika bps (felaktigt kallat baudrate i nästan alla sammanhang idag).
NMEA-tolkning:
http://www.maartenlamers.com/nmea/
http://arduiniana.org/libraries/TinyGPS/
http://www.arduino.cc/playground/Tutorials/GPS
http://www.arduino.cc/cgi-bin/yabb2/YaB ... 166042147/
Re: Nybörjarfrågor Atmega168 och GPS-modul
Jag har fortfarande inte löst detta pga. att jag försöker lära mig mera grunder först, jag är som sagt nybörjare och ska spara detta projekt ett tag. Jag tackar för all info jag har fått hittills och återkommer när jag ska fortsätta projektet - eller med en förklaring på hur jag löst det om så blir fallet.
jadler: Superbra information! Det är till stor hjälp
jadler: Superbra information! Det är till stor hjälp

Re: Nybörjarfrågor Atmega168 och GPS-modul
Låter som en sund inställning. det är mycket enklare att förstå vad so händer när man har en grundläggande förståelse för funktionen... vad som sker, hur och varför. Nu ver jag inte vilka grunder du behöver mer av, men man behöver både allmän elektronik (lite kretsteori och ohms lag mm, transistorers funktion, förstärkare osv.) , digitalteknik (logiska grindar och nät) samt till sist en mikroprocessors uppbyggnad och funktion. Parallellt med det kan man med fördel göra små assemblerprogram för att testa och förstå hur saker hänger ihop.
lycka till.
lycka till.
Re: Nybörjarfrågor Atmega168 och GPS-modul
Nu har jag läst Per Foyers bok "Microprocessoerteknik" och förstår lite mer hur det hänger ihop. Där fanns en exempelkod för att använda UART men inget om USART. Den är skriven i assembler vilket det kändes helt okej att lära sig lite av, lättare att förstå än C.
I alla fall så använder exemplet en AT90S8515 och jag har en Atmega168 så registren skiljer en del. Jag testade att kompilera koden rakt av men fick 9 st error och 2 varningar. Nu har jag ändrat koden så att registren stämmer med Atmega168:an(tycker jag i alla fall, men nått är bevisligen fel). Hela koden är lång men det är andra raden i den här delen som ger det sista error:et:
Orginalkod för AT90S8515:
Mitt försök till anpassning för Atmega168:
Detta ger error: invalid register på UCSR0A
Orginalkoden använder "sbis" och "in" vilket tydligen inte ska gå på I/O-register över 0x3F. Jag har istället använt "sbrs" och "lds" eftersom UCSR0A ligger på 0xC0. Är jag helt ute och cyklar?
Initieringen ser ut såhär:
Behövs mer information om resten av koden för att kunna svara på vart felet ligger?
I alla fall så använder exemplet en AT90S8515 och jag har en Atmega168 så registren skiljer en del. Jag testade att kompilera koden rakt av men fick 9 st error och 2 varningar. Nu har jag ändrat koden så att registren stämmer med Atmega168:an(tycker jag i alla fall, men nått är bevisligen fel). Hela koden är lång men det är andra raden i den här delen som ger det sista error:et:
Orginalkod för AT90S8515:
Kod: Markera allt
GetChar_UART_POLL: ; Pollar att data har anlänt
sbis USR, RXC ; Om data inte anlänt
rjmp GetChar_UART_POLL ; väntar vi...
in UARTchar, UDR0 ; annars läser vi mottaget
ret ; data och returnerar
Kod: Markera allt
GetChar_UART_POLL: ; Pollar att data har anlänt
sbrs UCSR0A, RXC0 ; Om data inte anlänt
rjmp GetChar_UART_POLL ; väntar vi...
lds UARTchar, UDR0 ; annars läser vi mottaget
ret ; data och returnerar
Orginalkoden använder "sbis" och "in" vilket tydligen inte ska gå på I/O-register över 0x3F. Jag har istället använt "sbrs" och "lds" eftersom UCSR0A ligger på 0xC0. Är jag helt ute och cyklar?
Initieringen ser ut såhär:
Kod: Markera allt
Initiera_UART_POLL:
ldi Temp, SPEED4800 ; Sätt prescalervärdet i
out UBRR0, Temp ; UART:ens UBBR-register och
ldi Temp, (1<<TXEN0)|(1<<RXEN0) ; aktivera därefter
sts UCSR0B, Temp ; mottagare och sändare
ldi Temp, (1<<USBS0)|(3<<UCSZ00); Set frame format: 8data, 2stop bit
sts UCSR0C, Temp
ret ; varpå initieringen är klar
Re: Nybörjarfrågor Atmega168 och GPS-modul
> Där fanns en exempelkod för att använda UART men inget om USART.
Inge större skillnad för det du vill göra. Det räcker dock sannolikt inte med att bara
ändra namnen på registren, du får nog kolla att de också fungerar på liknande sätt.
Det du har fastnat på verkar vara just det att nyare AVR har vissa register på högre
adresser där äldre modeller hade motsvarande register på lägre, och då fungerar inte
samma instruktioner. Jag har för mig att det var någon annan som hade just samma
problem med just USART registren för något halvår sedan. Det finns några macro's
som går runt detta genom att lägga generera olika instruktioner beroende på den
aktuella adressen för registret.
> Behövs mer information om resten av koden för att kunna svara på vart felet ligger?
Vilket "fel" ? (Så svaret är alltså "ja"...)
> ; Pollar att data har anlänt
Jag vet inte riktigt vad det är du ska göra, men generellt är det snyggare och
effektivare att låta USART modulen generera ett interrupt.
> ldi Temp, SPEED4800 ; Sätt prescalervärdet i
Var kommer "SPEED4800" från ?
Är den symbolen beräknad med hänsyn till processorhastigheten ?
Inge större skillnad för det du vill göra. Det räcker dock sannolikt inte med att bara
ändra namnen på registren, du får nog kolla att de också fungerar på liknande sätt.
Det du har fastnat på verkar vara just det att nyare AVR har vissa register på högre
adresser där äldre modeller hade motsvarande register på lägre, och då fungerar inte
samma instruktioner. Jag har för mig att det var någon annan som hade just samma
problem med just USART registren för något halvår sedan. Det finns några macro's
som går runt detta genom att lägga generera olika instruktioner beroende på den
aktuella adressen för registret.
> Behövs mer information om resten av koden för att kunna svara på vart felet ligger?
Vilket "fel" ? (Så svaret är alltså "ja"...)
> ; Pollar att data har anlänt
Jag vet inte riktigt vad det är du ska göra, men generellt är det snyggare och
effektivare att låta USART modulen generera ett interrupt.
> ldi Temp, SPEED4800 ; Sätt prescalervärdet i
Var kommer "SPEED4800" från ?
Är den symbolen beräknad med hänsyn till processorhastigheten ?
Re: Nybörjarfrågor Atmega168 och GPS-modul
Okej
Med "felet" menar jag error: invalid register
I boken så fanns det bara exempel på Pollning + att det stod att det var lättare att förstå. Det beskrivs lite om UART, styrd via interrupt och det lät ju smidigare men jag tänkte försöka få exempelkoden att fungera först. Klarar jag inte det så klarar jag troligtvis inte av att få det att fungera utan exempelkod.
SPEED4800 är satt till 103 som är räknat på 8MHz klocka
Angående registret UCSR0A så har jag hittat ett macro på AVRfreaks(det påstås att man ska lägga på 0x20 på registerplatsen när man refererar till register och inte I/O-port) som jag har testat med också men med samma resultat som innan:
Det jag vill göra i första hand är att få exempelkoden att fungera på Atmega168. Sedan använda koden för mottagning till att komma åt signalerna från GPS-modulen.
Hela koden ser ut såhär(Det mesta är författat av Per Foyer), jag har ändrat div. namn på registren för att anpassa till 168:an:
Med "felet" menar jag error: invalid register
I boken så fanns det bara exempel på Pollning + att det stod att det var lättare att förstå. Det beskrivs lite om UART, styrd via interrupt och det lät ju smidigare men jag tänkte försöka få exempelkoden att fungera först. Klarar jag inte det så klarar jag troligtvis inte av att få det att fungera utan exempelkod.
SPEED4800 är satt till 103 som är räknat på 8MHz klocka
Angående registret UCSR0A så har jag hittat ett macro på AVRfreaks(det påstås att man ska lägga på 0x20 på registerplatsen när man refererar till register och inte I/O-port) som jag har testat med också men med samma resultat som innan:
Kod: Markera allt
;================================================================
; Roland Kruse Macro - Start
;================================================================
.macro inx
.if @1 < 0x40
in @0, @1
.elif @1 >= 0x60
lds @0,@1
.else
.error "inx: Invalid I/O register address"
.endif
.endmacro
; Usage: OUTX address, reg
; use insted of OUT/STS
.macro outx
.if @0 < 0x40
out @0, @1
.elif @0 >= 0x60
sts @0,@1
.else
.error "outx: Invalid I/O register address"
.endif
.endmacro
;================================================================
; Roland Kruse Macro - End
;================================================================
Hela koden ser ut såhär(Det mesta är författat av Per Foyer), jag har ändrat div. namn på registren för att anpassa till 168:an:
Kod: Markera allt
.NOLIST
.INCLUDE "m168def.inc"
.LIST
.LISTMAC
.EQU LF = 10 ; ASCII för ny rad (Line Feed)
.EQU CR = 13 ; ASCII för vagnretur (Carriage Return)
.EQU EOL = 0 ; Markör för radslut (EndOfLine)
.EQU BUFFERSIZE = 25 ; Storlek på databuffer i SRAM
.EQU ANTALREPS = 5 ; Antal gånger att skriva användarens namn
.EQU SPEED4800 = 103 ; Prescaler för UART för 4800bpm (@8MHz)
.EQU PMSTART = 0x10 ; Ovanför avbrottsvektorerna
.DEF LPMdata = r0 ; Register för data som läses med lpm (LoadProgramMemory)
.DEF Temp = r16 ; Generellt temporärt register
.DEF UARTchar = r17 ; Register för data till och från UART
.DEF ReadCount = r18 ; Register för antal lästa teken
.MACRO PRINT_TEXT_UART
ldi ZL, LOW(@0*2)
ldi ZH, HIGH(@0*2)
rcall PutTextLine_UART
.ENDMACROA
;---------------------------------------------------------------------------
; Dataarea i SRAM
;---------------------------------------------------------------------------
.DSEG
.ORG 0x100
InBuffer: .BYTE BUFFERSIZE
;---------------------------------------------------------------------------
; Data och programkod i PM
;---------------------------------------------------------------------------
.CSEG
.ORG 0000 ; Abrottsvektortabell. Endast
rjmp Reset ; Reset-vektorn används.
.ORG PMSTART
;---------------------------------------------------------------------------
; Textsträngar (i PM) som skrivs ut av programmet
;---------------------------------------------------------------------------
TEXT1: .DB "Hej, vad heter du?", CR, LF, EOL, EOL ; 22 bytes
TEXT2: .DB "Trevligt att se dig, ", EOL ; 22 bytes
TEXT3: .DB "Jag repeterar ditt namn! :-)", CR, LF, EOL, EOL ; 32 bytes
NyRad: .DB CR, LF, EOL, EOL ; 4 bytes
;---------------------------------------------------------------------------
; Start (Vid Restet)
;---------------------------------------------------------------------------
Reset:
ldi Temp, LOW(RAMEND) ; Initiera stackpelare till
out SPL, Temp ; den högsta adressen i SRAM
ldi Temp, HIGH(RAMEND)
out SPH, Temp
rcall Initiera_UART_POLL ; Anropa rutin för UART-initiering
;---------------------------------------------------------------------------
; Main - Huvudprogram. Konverserar artigt med användaren (via
; serieporten). OBS! Inget echo på inmatad text. Terminalen kan
; sättas i "local echo" för att se vilka teken som ges.
;---------------------------------------------------------------------------
Main:
PRINT_TEXT_UART NyRad ; Börja med att mata fram tom rad
PRINT_TEXT_UART Text1 ; Skriv ut inledande hälsning
rcall GetBuffer_UART ; Läs in användarens namn
PRINT_TEXT_UART NyRad ; Mata fram en ny rad
PRINT_TEXT_UART Text2 ; Skriv nästa hälsning
rcall PutBuffer_UART ; följt av användarens (inmatade) namn
PRINT_TEXT_UART NyRad ; och därefter radmatning.
PRINT_TEXT_UART Text3 ; Skriv ut nästa text
ldi Temp, ANTALREPS ; repetera det inmatade namnet...
RepeteraNamn:
rcall PutBuffer_UART ; ...genom att upprepat skriva ut det
PRINT_TEXT_UART NyRad ; med en radframmatning efter varje
dec Temp ; Minska repetitionsräknare
brne RepeteraNamn ; Fortsätt om ej noll
rjmp Main ; Upprepa allt från början
;---------------------------------------------------------------------------
; PutTextLine_UART - Skriv en textsträng via UART.
; Parametrar: Z = Adress till textsträng i PM
;---------------------------------------------------------------------------
PutTextLine_UART:
lpm ; Hämta tecken från PM (r0 <- (Z) )
mov UARTchar, LPMdata ; Kopiera läst tecken
cpi UARTchar, EOL ; Är det slut på strängen?
Breq EndPutLineText ; Ja, dags att avsluta utskriften
rcall PutChar_UART_POLL ; Inte slut så skriv ut tecken
adiw ZL, 1 ; Peka på nästa tecken i textsträngen
rjmp PutTextLine_UART ; och fortsätt
EndPutLineText:
ret ; Inga fler tecken att skriva ut så returnera
;---------------------------------------------------------------------------
; PutBuffer_UART - Skriv ut innehållet i InBuffer via UART.
; Parametrar: X = Adress till buffer i SRAM
;---------------------------------------------------------------------------
PutBuffer_UART:
ldi XL, LOW(InBuffer) ; Registerparet X används för att
ldi XH, HIGH(InBuffer) ; adressera bufferten i SRAM
PutBuffer_Next:
ld UARTchar, X+ ; Hämta tecken och stega fram till nästa
cpi UARTchar, EOL ; Är det slut på tecken i buffer?
breq PutBuffer_Done ; Ja, dags att sluta skriva
rcall PutChar_UART_POLL ; Nej, skriv ut tecken
rjmp PutBuffer_Next ; och börja om med nästa
PutBuffer_Done:
ret ; Inga fler tecken så returnera
;---------------------------------------------------------------------------
; GetBuffer_UART - Läs en teckensträng från UART och lagra i InBuffer
; En inläst sträng anses avslutas med CR. Rutinen
; kollar att inte fler tecken än vad som ryms i
; buffern läses in.
; Parametrar: X = pekare till buffer i SRAM
;---------------------------------------------------------------------------
GetBuffer_UART:
ldi XL, LOW(InBuffer) ; Registerparet X används för att
ldi XH, HIGH(InBuffer) ; adressera bufferten i SRAM
clr ReadCount ; Inga tecken har lästs in ännu
GetBuffer_Next:
rcall GetChar_UART_POLL ; Läs ett tecken från UART
cpi UARTchar, CR ; Är det slut på raden?
Breq GetBuffer_Done ; Ja, dags att sluta läsa tecken
st X+, UARTchar ; Fler tecken så lagra tecken och stega
inc ReadCount ; fram pekaren samt öka teckenräknaren.
cpi ReadCount, BUFFERSIZE ; Ryms antalet tecken i buffern?
Brlt GetBuffer_Next ; Ja, vi fortsätter...
GetBuffer_Done:
ldi UARTchar, EOL ; Markera radslut i inbuffer
st X, UARTchar ; genom att skriva EOL efter inmatad text
ret ; Returnera från subrutin
;---------------------------------------------------------------------------
; Initiera_UART_POLL (Lågnivå) - Initiera UART för pollad I/O
;---------------------------------------------------------------------------
Initiera_UART_POLL:
ldi Temp, SPEED4800 ; Sätt prescalervärdet i
sts UBRR0L, Temp ; UART:ens UBBR-register och
ldi Temp, (1<<TXEN0)|(1<<RXEN0) ; aktivera därefter
sts UCSR0B, Temp ; mottagare och sändare
ldi Temp, (1<<USBS0)|(3<<UCSZ00); Set frame format: 8data, 2stop bit
sts UCSR0C, Temp
ret ; varpå initieringen är klar
;---------------------------------------------------------------------------
; GetChar_UART_POLL - (Lågnivå) Vänta på tecken i UDR (BUSY WAIT)
; Returnerar: UARTchar (Läst tecken)
;---------------------------------------------------------------------------
GetChar_UART_POLL: ; Pollar att data har anlänt
sbrs UCSR0A, RXC0 ; Om data inte anlänt
rjmp GetChar_UART_POLL ; väntar vi...
lds UARTchar, UDR0 ; annars läser vi mottaget
ret ; data och returnerar
;---------------------------------------------------------------------------
; PutChar_UART_POLL - (Lågnivå) Skriv tecken till UDR (när tomt)
; Parametrar: UARTchar (Data att sända)
;---------------------------------------------------------------------------
PutChar_UART_POLL: ; Pollar att UDR är tomt
sbrs UCSR0A, UDRE0 ; Om UDR inte tomt ännu
rjmp PutChar_UART_POLL ; väntar vi...
sts UDR0, UARTchar ; och lägger sedan data i UDR
ret
Re: Nybörjarfrågor Atmega168 och GPS-modul
sbrs kan bara använda på register r0 till r31.
Så du måste "kopiera" UCSR0A till ett register.
exempel:
Hoppas att det var till någon hjälp 
Så du måste "kopiera" UCSR0A till ett register.
exempel:
Kod: Markera allt
GetChar_UART_POLL: ; Pollar att data har anlänt
lds r16,UCSR0A
sbrs r16,RXC0
rjmp GetChar_UART_POLL ; väntar vi...
lds UARTchar, UDR0 ; annars läser vi mottaget
ret
