身為一個奉公守法的飆車族,
能夠知道自己真正的車速是一件很重要的事情,
而你永遠不會知道自己的儀表板的快樂程度,
所以就用GPS測定自己的車速了。
在露天上面可以找到許多UART界面的GPS模組,
上電之後,接收器就會自動連上衛星,並且透過Serial Port不斷的傳回數據,
這些數據為NMEA所制定的訊息格式,
因此直接使用TinyGPS++這個Arduino的函式庫來解讀GPS接收器所傳回來的訊息。
GPS除了可以讀回車速,也可傳回許多其他的相關資訊,
而這邊我總共抓取了車速、UTC時間、航向、HDOP(水平2D坐標精度因子) 以及所連接的衛星數量這五個參數。
為了易於判讀資訊以及減小體積,這邊只用一個四位數的七段顯示器來顯示資訊,
搭配Atmega8l上的兩個外部中斷,來調整顯示模式以及顯示亮度。
目前Atmega8l幾乎是市面上最便宜的Atmega系列晶片,甚至比有些attiny還來的便宜,
但也因為8k的flash,所以必須想盡辦法降低程式碼的體積,
因此使用了AVR-GCC直接對IO PORT存取的語法來點亮七段顯示器,
這邊把原本用來接外部振盪器的PB6,PB7也作為GPIO使用。
也為了搭配車上的12V電源,或者9V電池,所以另外規劃了一個5伏特的穩壓。
正面左邊為GPS模組,中間是共陽四位七段顯示器,
右邊兩個中斷的按鈕、2.1DC座以及開關。
背面分別為穩壓IC、Atmega8l以及GPS的陶瓷天線。
為了減小體積,Atmega8l直接焊死到板子上,
所以另外拉出了ICSP腳位方便之後修改程式碼。
顯示時間八點整,靠近七段顯示器的按鈕為中斷1(PD3,arduino pin3),調整亮度用。
另一個按鈕則為中斷0(PD2, arduino pin2),用來改變顯示模式。
Ublox neo 6m GPS module
程式碼:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <TinyGPS++.h> | |
byte displayMode=1; //(mode 1~mode 5) | |
int displayNum; | |
byte pointDigit; | |
byte brightness=205;//the lower, the more bright | |
byte numPattern[10]={ | |
B11000000,//0 | |
B11111001,//1 | |
B10100100,//2 | |
B10110000,//3 | |
B10011001,//4 | |
B10010010,//5 | |
B10000010,//6 | |
B11011000,//7 | |
B10000000,//8 | |
B10010000,//9 | |
}; | |
TinyGPSPlus gps; | |
void setup() { | |
DDRB = B11111111; | |
DDRC = B1111; | |
pinMode(2,INPUT_PULLUP); | |
pinMode(3,INPUT_PULLUP); | |
attachInterrupt(1, brightnessISR, RISING); | |
attachInterrupt(0, nextModeISR, RISING); | |
Serial.begin(9600);//GPS receiver's baud rate | |
} | |
void loop() { | |
lightUp(displayNum,pointDigit); | |
} | |
void lightUp(int num, byte point){ | |
PORTC=B0001;//LSB | |
if(point==0)PORTB=numPattern[num%10]&127; | |
else PORTB=numPattern[num%10]; | |
lightPeroid(); | |
PORTC=B0010; | |
if(point==1)PORTB=numPattern[(num/10)%10]&127; | |
else PORTB=numPattern[(num/10)%10]; | |
lightPeroid(); | |
PORTC=B0100; | |
if(point==2)PORTB=numPattern[(num/100)%10]&127; | |
else PORTB=numPattern[(num/100)%10]; | |
lightPeroid(); | |
PORTC=B1000;//MSB | |
if(point==3)PORTB=numPattern[(num/1000)%10]&127; | |
else PORTB=numPattern[(num/1000)%10]; | |
lightPeroid(); | |
} | |
void lightPeroid(){ | |
int t=1000;//digit period:1000us | |
delayMicroseconds(t-brightness*4); | |
PORTC=B0000; | |
PORTB=B11111111; | |
delayMicroseconds(brightness*4); | |
} | |
void serialEvent(){ | |
double kmhr; | |
while(Serial.available()){ | |
if(gps.encode(Serial.read())){ | |
switch(displayMode){ | |
case 1://UTC time(+8 in Taiwan) | |
displayNum=((gps.time.hour()+8)%24)*100+gps.time.minute(); | |
pointDigit=2; | |
break; | |
case 2: //speed(kilometer per hour) | |
kmhr=gps.speed.kmph(); | |
displayNum=(byte)(kmhr*10); | |
pointDigit=1; | |
break; | |
case 3: //heading course degree (0~360 degree) | |
displayNum=gps.course.deg(); | |
pointDigit=0; | |
break; | |
case 4://GPS accuracy(HDOP:0.00~99.99) | |
displayNum=gps.hdop.value(); | |
pointDigit=2; | |
break; | |
case 5://number of connected satellite(0~12) | |
displayNum=gps.satellites.value(); | |
pointDigit=0; | |
break; | |
} | |
} | |
} | |
} | |
void nextModeISR(){ | |
static unsigned long last_interrupt_time = 0; | |
unsigned long interrupt_time = millis(); | |
if (interrupt_time - last_interrupt_time > 150){ | |
displayMode++; | |
if(displayMode==6)displayMode=1; | |
} | |
last_interrupt_time = interrupt_time; | |
} | |
void brightnessISR(){ | |
static unsigned long last_interrupt_time = 0; | |
unsigned long interrupt_time = millis(); | |
if (interrupt_time - last_interrupt_time > 300) { | |
brightness-=25; | |
if(brightness<=60)brightness=205; | |
} | |
last_interrupt_time = interrupt_time; | |
} | |
compile後的大小
測試影片:
--------------------
GPS模組-----------------690
Atmega8l---------------39 共陽四位七段顯示器--50
按鈕
開關
穩壓IC7805
電容104
2.1mm DC jack
洞洞板
接線
-----------------
TinyGPS++
NMEA Reference Manual
u - blox 6 GPS Modules
Atmega8
Arduino IDE 1.0.1 and ATmega8 running at 8MHz with Optiboot