2012-05-14 LTH RAPPORT: DIGITALA PROJEKT Linus Ahlberg, Johan Ryde och Kristin Svensson I-09
Innehåll Inledning... 3 Krav... 3 Ursprungliga krav... 3 Modifierade krav (ej trådlös)... 3 Konstruktion... 4 Fjärrkontrollen... 4 Bilen... 4 Hårdvara... 4 Accelerometer ADXL-3xx... 4 Processor ATMEGA32 samt ATMEGA16... 4 Radiosändare/mottagare MOD24LR (användes ej)... 5 Motorer... 5 H-brygga L298N... 5 Mjukvara... 5 Problem... 5 Slutsats... 6 Appendix 1.1 Kod till fjärrkontrollen... 7 Appendix 1.2 Kod till bilen... 9 Appendix 1.3 Gemensam kod till radiosändaren. OBS: fungerade ej.... 14 2
Inledning Som projekt valde vi att bygga en radiostyrd bil som skulle styras med hjälp av en fjärrkontroll som mäter vilken vinkel man håller den i. Olika vinklar skulle ge bilen olika signaler och på så sätt skulle man kunna ändra riktning och hastighet på bilen. För att genomföra detta behövde vi två olika enheter, en handhållen enhet och en bil. Dessa var båda tvungna att vara utrustade med en processor samt en radiosändare, för att möjliggöra kommunikation mellan dem. Efter problem med att få radiosändarna att fungera fick vi dock ändra vår ursprungliga plan, och istället använda oss av sladdar mellan fjärrkontrollen och bilen för att skicka styrsignalerna. Dessutom krävdes det en komponent som gjorde det möjligt för systemet att avläsa hur användaren vinklar fjärrkontrollen. Här valde vi för enkelhetens skull att använda oss av en accelerometer. Bild 1. Den färdiga bilen med kontroll Krav Ursprungliga krav 1. Bilen ska trådlöst gå att fjärrstyra med en fjärrkontroll. 2. Kontrollen ska fungera så att bilen svänger åt det håll fjärrkontrollen lutas. 3. Gas framåt och bakåt ska dessutom regleras på samma sätt, det vill säga då kontrollen lutas framåt ska bilen köra framåt. 4. Bilen ska kunna köra och svänga i olika hastigheter. Modifierade krav (ej trådlös) 1. Bilen ska kunna styras med en fjärrkontroll. 2. Kontrollen ska fungera så att bilen svänger åt det håll fjärrkontrollen lutas. 3. Gas framåt och bakåt ska dessutom regleras på samma sätt, det vill säga då kontrollen lutas framåt ska bilen köra framåt. 4. Bilen ska kunna köra och svänga i olika hastigheter. 3
Konstruktion Fjärrkontrollen Kontrollen är utrustad med en accelerometer som skickar signaler till processorn. Dessa signaler behandlas sedan av processorns AD-omvandlare och på så sätt tolkar processorn vilken vinkel fjärrkontrollen är lutad i. Vi gör detta i två steg genom att först mäta vinkel i en led och därefter i en annan. Detta kan vi tolka som x-led och y-led (bakåt/framåt resp. vänster/höger). I processorn omvandlas sedan dessa båda vinklar till ett av 20 olika lägen. Varje läge står för ett visst spann av olika vinkelpar som ska ge olika hastigheter på motorerna. Detta läge vidarebefordras därefter som ett tal till själva bilen. Se tabellen nedan för översiktlig förklaring av de 20 lägena. Hastigheter Full vänster Halv vänster Ingen sväng Halv höger Full höger Full framåt 16 17 18 19 20 Halv framåt 11 12 13 14 15 Stilla/roterar 6 7 8 9 10 Bakåt 1 2 3 4 5 Bilen Bilen mottar en signal från kontrollen som består av ett tal mellan 1 och 20. Bilens processor behandlar sedan den signalen och översätter den till riktning och hastighet av vänster samt höger motor. När detta är gjort vidarebefordrar den resultatet till h-bryggan som styr bilens motorer. Hårdvara Accelerometer ADXL-3xx Accelerometern är den komponent som gör det möjligt att tolka hur fjärrkontrollen är lutad. Vår Bild 2. Bilen accelerometer kan arbeta i både X, Y och Z-led. För vår konstruktion används bara X och Y-led där X anger hur bilen ska svänga och Y om den ska köra framåt eller bakåt. Accelerometern är analog och visar därför en gradering för hur stort utslaget är, vilket gör det möjligt att ange olika hastigheter för olika stor lutning på kontrollen. Den är dock inte speciellt noggrann, visst brus förekommer även om man håller den stilla. Processor ATMEGA32 samt ATMEGA16 I projektet använder vi oss av två olika processorer med den enda väsentliga skillnaden att den ena har 32kB programminne och den andra 16kB. Vårt projekt hade dock fungerat precis lika bra med två stycken atmega16-processorer. Processorerna är utrustade med AD-omvandlare och har en klockfrekvens på 16MHz och 1kB RAM-minne. Detta är nödvändigt för den processor som används på fjärrkontrollen eftersom signalerna från accelerometern är analoga och måste digitaliseras för att kunna användas. Dessutom har processorerna stöd för så kallat SPI (Serial Peripheral Interface), vilket vi använt oss av för att göra överföringar mellan enheterna. 4
Radiosändare/mottagare MOD24LR (användes ej) För kommunikationen tänkte vi använda två kombinerade radiosändare/mottagare där den som satt på bilen i huvudsak var konfigurerad som mottagare och den som satt på fjärrkontrollen i huvudsak var konfigurerad som sändare. Med avseende till SPI så konfigurerades båda processorerna som master och sändar-/mottagarenheterna som slave. Radiosändaren som används kan konfigureras på många olika sätt med olika funktioner, ex. skicka effortless eller inte, skicka till en specifik adress eller bara broadcasta. De många finesserna i radiosändaren som vi egentligen inte behövde använda, var en av anledningarna till att vi inte fick den till att fungera som vi tänkt oss. Den var helt enkelt för avancerad. Motorer I projektet används två stycken 6v likströmsmotorer som styrs av h-bryggan. Motorerna driver var sitt hjul vilket gör det möjligt att driva hjulen framåt eller bakåt oberoende av varandra. H-brygga L298N Eftersom motorerna inte går att driva på den ström som processorn klarar av att förse den med använder vi oss av en h-brygga. På bryggan kan man nämligen koppla in en extern strömkälla som används för att driva motorerna. Bryggan gör det även möjligt att driva de två olika hjulen både framåt och bakåt i olika hastigheter och helt oberoende av varandra, detta utnyttjas för att kunna styra bilen då vi inte har något styrservo. Mjukvara I Appendix 1.1 och 1.2 finns den källkod, skriven i C, som nu är inbyggd i processorerna. Koden är skriven så att fjärrkontrollen ska kunna motta signaler från accelerometern och göra en ADomvandling av dessa. Därefter ska processorn räkna om signalerna till de styrsignaler som sedan skickas till bilen. Bilen ska ta emot styrsignalerna och översätter dessa till de slutliga signaler till h- bryggan som gör att motorerna driver hjulen åt rätt håll. I Appendix 1.3 finns den källkod som skrevs till radiosändarna, men som vi inte fick att fungera. Problem De största problemen som vi har stött på under projektets gång var till att börja med i vilken ände man skulle börja. Detta berodde på att ingen av gruppmedlemmarna har någon tidigare erfarenhet av projekt av den här typen och därför inte riktigt visste vilken typ av komponenter som behövdes. På mjukvarusidan saknade vi dessutom kunskaper, dels i C-språket, men kanske framför allt mer övergripande kunskaper om hur man styr och programmerar en processor och andra elektroniska komponenter. Efter uppstartsfasen kunde vi efter en del arbete konstatera att vi hade en defekt accelerometer. Detta tog längre tid att upptäcka än det borde ha gjort, på grund av vår ovana inom elektronik, vilket gjorde att arbetet försenades en del. Det största problemet som vi stötte på visade sig dock vara komplexiteten i att konfigurera radiosändarna och få dem att fungera på rätt sätt. Efter att ha lagt ner åtskilliga timmar på att få den trådlösa kommunikationen att fungera bestämde vi oss för att vi inte skulle hinna få de att fungera i tid för projektets slut. Vi valde därför att istället skapa en förbindelse direkt mellan de båda processorerna via sladdar. 5
Slutsats Även om vi hade ganska mycket svårigheter med diverse komponenter genom projektet så har vi ändå lyckats lära oss en hel del om elektronik och hur man kan styra andra mindre komponenter med hjälp av en microcontroller (processor). Om vi hade gjort om projektet så skulle vi framförallt ha börjat med att få handkontroll och bil att fungera med hjälp av förbindelse med sladd och först därefter börjat med radiosändarna. När vi nu istället direkt valde att försöka få radiosändarna att fungera så blev det lite övermäktigt att försöka förstå både hur radiosändarna kommunicerade samt hur processorerna kommunicerade med radiosändarna (via SPI). Hade vi kunnat först försäkra oss om att SPI-överföringen fungerade på rätt sätt genom att ha förbindelse med sladd mellan processorerna så hade vi kunnat reducera arbetet med att få radiosändarna att fungera ganska kraftigt. I övrigt gick projektet relativt bra och vi är nöjda med att ha lyckats göra några saker som inte så många grupper före oss i kursen har gjort, särskilt att använda två stycken processorer, samt att använda oss av en accelerometer. 6
Appendix 1.1 Kod till fjärrkontrollen #include < avr / io.h > #define CSN_LOW() PORTB &= ~ (1 << PB4); //slave select låg #define CSN_HIGH() PORTB = (1 << PB4); //slave select hög #define REDLED_ON() PORTB = (1 << PB0); #define REDLED_OFF() PORTB &= ~ (1 << PB0); #define REDLED_FLIP() PORTB ^= (1 << PB0); /* SPI initiering för master */ void SPI_MasterInit(void) { DDRB = (1 << PB4) (1 << PB5) (1 << PB7) (1 << PB0); DDRB &= ~ (1 << PB6); SPCR = (1 << SPE) (1 << MSTR) (1 << SPR0) (1 << SPR1); /*Kollar vinkel i y-led från accelerometer. För att göra dett görs en * A/D-konvertering. Resultatet skickas tillbaka som en int mellan 1-4. */ int checkyangle() { ADMUX = 0x20; ADCSRA = 0x40; while (!(ADCSRA & (1 << ADIF))); unsigned short int angle = ADCH; //läser från AD-konv och sparar i variabeln angel ADCSRA = (1 << ADIF); if (angle <= 0x50) { return 1; if (angle <= 0x56 && angle > 0x50) { return 2; if (angle <= 0x59 && angle > 0x56) { return 3; if (angle > 0x59) { return 4; return 1; /*Kollar vinkel i x-led från accelerometer. För att göra dett görs en * A/D-konvertering. Resultatet skickas tillbaka som en int mellan 1-5. */ int checkxangle() { ADMUX = 0x21; ADCSRA = 0x40; while (!(ADCSRA & (1 << ADIF))); unsigned short int angle = ADCH; //läser från AD-konv och sparar i variabeln angel ADCSRA = (1 << ADIF); if (angle <= 0x4E) { return 1; 7
if (angle <= 0x50 && angle > 0x4E) { return 2; if (angle <= 0x56 && angle > 0x50) { return 3; if (angle <= 0x59 && angle > 0x56) { return 4; if (angle > 0x59) { return 5; return 0; /* Anropar beräkning av nuvarande vinkel i både x- och y-led, samt * räknar ut vilket läge (1-20) som vinkelparet uppfyller. */ unsigned short int calculatetransmit() { unsigned short int x = checkxangle(); unsigned short int y = checkyangle(); unsigned short int calc = ((y - 1) * 5 + x); return calc; /* * Skickar nuvarande läge (1-20) till bilen. */ unsigned short int SPI_transmit(unsigned short int transmit) { CSN_LOW(); //slave select låg SPDR = transmit; //skriver transmit till SPDR för att starta överföringen via SPI while (!(SPSR & (1 << SPIF))); //väntar till lyckad sändning CSN_HIGH(); //slave select hög return transmit; main() { REDLED_ON(); DDRB = (1 << PB0); //Diod SPI_MasterInit(); ADCSRA = 0x80; CSN_HIGH(); unsigned short int transmit; while (1) { transmit = calculatetransmit(); SPI_transmit(transmit); if (transmit!= 8) { REDLED_OFF(); else { REDLED_ON(); 8
Appendix 1.2 Kod till bilen #include < avr / io.h > #include < avr / interrupt.h > #include < util / delay.h > int speedrefresh, delayh, delayv, speedcounth, speedcountv, updatecount, tempflip; unsigned short int recieve; #define CSN_LOW() PORTB &= ~ (1 << PB4); #define CSN_HIGH() PORTB = (1 << PB4); #define GREENLED_ON() PORTD = (1 << PD5); #define GREENLED_OFF() PORTD &= ~ (1 << PD5); #define GREENLED_FLIP() PORTD ^= (1 << PD5); #define YELLOWLED_ON() PORTD = (1 << PD2); #define YELLOWLED_OFF() PORTD &= ~ (1 << PD2); #define clrinp() PORTB &= ~ (1 << PB0) (1 << PB1) (1 << PB2) (1 << PB3); //rensar alla inputs till bryggan #define L_B() PORTB = (1 << PB1); //input: vänster motor back #define L_F() PORTB = (1 << PB0); //input: vänster motor fram #define R_B() PORTB = (1 << PB2); //input: höger motor back #define R_F() PORTB = (1 << PB3); //input: höger motor fram void SPI_slaveInit(void) { //SPI initiate för slaven(bilen) DDRB = (1 << PB6); SPCR = (1 << SPE) (1 << SPR0); void setdelayv(int value) { //delayfunktion för hastighetsreglering vänster motor delayv = value; void setdelayh(int value) { //delayfunktion för hastighetsreglering höger motor delayh = value; void enginecontrol(void) //omtolkning av insignaler till motorsignaler { if (recieve > 0 && recieve < 21) { GREENLED_OFF(); switch (recieve) { case 1: R_B(); case 2: R_B(); case 3: 9
R_B(); L_B(); case 4: L_B(); case 5: L_B(); case 6: L_B(); setdelayv(1); setdelayh(1); case 7: L_B(); case 8: PORTB = 0x00; case 9: R_B(); setdelayv(1); setdelayh(1); case 10: R_B(); case 11: setdelayh(1); case 12: case 13: 10
case 14: case 15: ; setdelayv(1); case 16: setdelayv(1); setdelayh(0); case 17: setdelayh(1); case 18: setdelayv(1); setdelayh(1); case 19: setdelayv(1); case 20: setdelayv(0); setdelayh(1); else { GREENLED_ON(); //Grön lampa lyser => något är fel med mottagen variabel PORTB = 0x00; 11
unsigned short int SPI_recieve(void) { //funktion för att ta emot signaler från master while (!(SPSR & (1 << SPIF))); return SPDR; main() { DDRD = (1 << PD2); SPI_slaveInit(); DDRB = (1 << PB0) (1 << PB1) (1 << PB2) (1 << PB3); //Inputs för bryggan DDRD = (1 << PD0) (1 << PD1) (1 << PD2) (1 << PD5); //Enables + Dioder PORTD = (1 << PD0) (1 << PD1); //Enables TIMSK = 0x01; //inställning klockan TCCR0 = 0x01; //inställning klockan TCNT0 = 0; //inställning klockan updatecount, speedrefresh, speedcounth, speedcountv, delayh, delayv=0; GREENLED_ON(); sei(); //enable interrupts while (1) { //evighetsloop recieve = SPI_recieve(); //mottar signal från master enginecontrol(); //förmedlar signalen till motorerna if (recieve!= 8) { //kontroll YELLOWLED_ON(); else { YELLOWLED_OFF(); ISR(TIMER0_OVF_vect) //Varje avbrott från timern { // updatecount++; speedrefresh++; // if (updatecount == 30000) { //20000 ~ 1 Hz //kontrollera klockfrekvens mha lampa // updatecount = 0; // GREENLED_FLIP(); // if (speedrefresh == 20) { speedrefresh = 0; switch (delayh) { //sätter höger motors hastighet mha delays case 0: PORTD = (1 << PD1); case 1: speedcounth++; if (speedcounth < 1) { PORTD &= ~ (1 << PD1); else { PORTD = (1 << PD1); speedcounth = 0; case 2: speedcounth++; if (speedcounth < 2) { 12
PORTD &= ~ (1 << PD1); else { PORTD = (1 << PD1); speedcounth = 0; switch (delayv) { //sätter vänster motors hastighet mha delays case 0: PORTD = (1 << PD0); case 1: speedcountv++; if (speedcountv < 1) { PORTD &= ~ (1 << PD0); else { PORTD = (1 << PD0); speedcountv = 0; case 2: speedcountv++; if (speedcountv < 2) { PORTD &= ~ (1 << PD0); else { PORTD = (1 << PD0); speedcountv = 0; 13
Appendix 1.3 Gemensam kod till radiosändaren. OBS: fungerade ej. #include < util / delay.h > #define F_CPU 16000000 #include < avr / interrupt.h > #define CSN_LOW() PORTB &= ~ (1 << PB4); #define CSN_HIGH() PORTB = (1 << PB4); #define CE_LOW() PORTB &= ~ (1 << PB3); #define CE_HIGH() PORTB = (1 << PB3); #define REDLED_ON() PORTB = (1 << PB0); #define REDLED_OFF() PORTB &= ~ (1 << PB0); #define REDLED_FLIP() PORTB ^= (1 << PB0); #define R_REGISTER 0b00000000 #define W_REGISTER 0b00100000 #define R_RX_PAYLOAD 0b01100001 #define W_TX_PAYLOAD 0b10100000 #define FLUSH_TX 0b11100001 #define FLUSH_RX 0b11100010 #define NOP 0b11111111 #define CONFIG 0x00 #define EN_AA 0x01 #define EN_RXADDR 0x02 #define SETUP_AW 0x03 #define SETUP_RETR 0x04 #define RF_CH 0x05 #define STATUS 0x07 #define TX_ADDR 0x10 #define RX_PW_P0 0x11 #define ERX_P1 (0 << 1) #define RX_DR (1 << 6) #define TX_DS (1 << 5) #define RX_P_NO (1 << 3)(1 << 2)(1 << 1) #define ENAA_P0(0 << 0) #define ENAA_P1(0 << 1) #define ENAA_P2(0 << 2) #define ENAA_P3(0 << 3) #define ENAA_P4(0 << 4) #define ENAA_P5(0 << 5) #define MASK_RX_DR(1 << 6) #define MASK_TX_DS(1 << 5) #define EN_CRC(1 << 3) #define CRCO(1 << 2) #define PWR_UP(1 << 1) #define PRIM_RX(1 << 0) #define RX_DR(1 << 6) #define TX_DS(1 << 5) #define MAX_RT(1 << 4) #define RX_P_NO(1 << 3) (1 << 2) (1 << 1) #define TX_FULL(1 << 0) int transmitted, again; 14
void SPI_MasterInit(void) { DDRB = (1 << PB4) (1 << PB5) (1 << PB7) (1 << PB0); DDRB &= ~ (1 << PB6); SPCR = (1 << SPE) (1 << MSTR) (1 << SPR0) (1 << SPR1); uint8_t spio(uint8_t b) { SPDR = b; while (!(SPSR & (1 << SPIF))); return SPDR; uint8_t radio_status(void) { CSN_LOW(); uint8_t status = spio(nop); CSN_HIGH(); return status; uint8_t combo(uint8_t cmd, uint8_t reg, uint8_t data) { uint8_t compound, ret; CSN_LOW(); compound = cmd reg; ret = spio(compound); spio(data); CSN_HIGH(); return ret; void radio_setup(void) { DDRB = (1 << PB3) (1 << PB4); DDRB &= ~ (1 << PB2); CE_LOW(); radio_config_tx(); combo(w_register, RF_CH, 33); spio(w_register EN_AA); spio(enaa_p0); combo(w_register, EN_RXADDR, ERX_P1); combo(w_register, RX_PW_P0, 1); spio(flush_rx); spio(nop); uint8_t status = radio_status(); combo(w_register, STATUS, TX_DS); combo(w_register, STATUS, RX_DR); combo(w_register, STATUS, MAX_RT); // spio(w_register SETUP_RETR); // spio(0x00); // spio(w_register TX_ADDR); // spio(0x00); void radio_config_rx() { combo(w_register, CONFIG, PWR_UP CRCO EN_CRC PRIM_RX); void radio_config_tx() { 15
combo(w_register, CONFIG, PWR_UP CRCO EN_CRC); uint8_t radiotransmit(uint8_t send) { CSN_LOW(); uint8_t status = spio(w_tx_payload); spio(send); CSN_HIGH(); CE_HIGH(); _delay_us(128); CE_LOW(); return status; uint8_t radiorecieve(uint8_t recieved) { CSN_LOW(); uint8_t status = spio(r_rx_payload); spio(nop); CSN_HIGH(); return status; 16