Kla ggvisarskylt -med en underton av rassel av Jonathan So nnerup & Gabriel Jo nsson May 29, 2012
Abstract This report describes the creation of the Kläggnummerskylt, a project in the course Digital Projects EITF40. The main hardware components is a Atmega16 AVR processor and an old flip-dot matrix from a bus. The AVR was programmed in the texteditor Vim for Linux and was debugged using AVR Studio 4.
Innehållsförteckning 1 Inledning 2 2 Kravspecifikation 2 3 Metod 2 4 Implementation 3 4.1 Flip-Dot Matrix.......................... 3 4.2 Atmega AVR 16-bit....................... 3 4.3 Knappsats (Grayhill 88BB2-072)................ 3 4.4 Avkodare (Fairchild MM74C922N)............... 3 5 Resultat 3 6 Diskussion 4 6.1 Problem.............................. 4 6.2 Sammanfattning......................... 4 A Kretsschema 5 B Källkod 6 1
1 Inledning Kursen digitala projekt har som mål att självständigt, med viss hjälp från handledaren, skapa en fungerande prototyp. Vad som ska byggas är helt upp till studenterna. I det här fallet har en gammal bussnummerskylt satts ihop med en krets som gör det möjligt att mata in siffror som sedan ska skrivas ut på skylten. Skylten är menad sitta i E-sektionens sektionslokal Edekvata på LTH och har som huvudsyfte att visa vilket klägg (även kallat hambugare utanför studentkretsar) som serveras vid specificerat tillfälle. Själva projektet påbörjades med att vi inte hade en aning om vad vi skulle bygga. Efter mycket sökande om vad som vore praktiskt att bygga fick vi veta att den nuvarande kläggskylten var sönder och att ETF hade köpt in ett par bussnummerskyltar och projektet var igång. 2 Kravspecifikation På ett enkelt sätt visa numret som serveras Kunna styras med ett slags tangentbord Ha en liten LCD skärm för användaren som visar nuvarande nummer Kunna kommunicera via bluetooth / ethernet för remote access Ha finurliga hacks för nöjets skull 3 Metod Då det inte fanns någon dokumentation till skylten gick mycket av tiden i början till att förstå hur anslutningen (2 13 pinnar) till busskylten var kopplad. Detta uppnåddes genom resistansmätningar från den 26 pinnarna på ingången och benen på de tre multiplexrarna som finns på kretsen till skylten. Vidare förståelse uppnåddes genom prövning, alltså genom att sätta spänning på inpinnarna och se vad som hände på plattan. Efter att busskyltens funktioner dokumenterats påbörjades implementeringen av processorn som vi fick färdig att programmera med ett JTAG-interface och AVR studio 4. Detta visade sig vara ganska problematiskt då vi inte hade ägde några kunskaper i C-programmering och på grund av att vi läste datortekniken som egentligen är ett förkunskapskrav till kursen under kursens gång. Då vi använder oss av en knappsats för inmatning körs alla underprogram på interrupts som kontrolleras med en switch-sats. För att kunna få kontrollerade signaler från knappsatsen tillfogades en avkodare. Denna skickar binärt från fyra data ut pinnar ut vilken knapp som blivit nertryckt. 2
4 Implementation 4.1 Flip-Dot Matrix Huvudkomponenten i projektet, förutom processorn. Det är skyltens uppgift att visa vilket nummer som serveras just nu och detta styrs med hjälp av en 26 pinnars kontakt från skylten till processorn. Pixlarna i skylten är små magnetiska metallbrickor (16 28 st) som är antingen på (gul sida upp) eller av (svart sida upp). Dessa styrs med fruktansvärt många spolar som sitter runt små metallstavar. Det sitter två metallstavar per pixel och för att vända en pixel inducerar man ett elektriskt fält i spolarna som gör stavarna magnetiska och pixeln kan vändas. 4.2 Atmega AVR 16-bit Hjärnan som styr hela prototypen. Den hämtar indata från knappstasen, utför olika processer beroende på vilken knapp som valdes, och skickar ut data till skylten. 4.3 Knappsats (Grayhill 88BB2-072) För att kunna ge processorn indata användes en 4-bitars knappsats med siffrorna 0-9 och bokstäverna A-F. Vi använde alla knappar på knappsatsen där vi lät siffrorna vara siffror och bokstäverna blev styrsignaler till processorn, bland annat Enter, +, - och och andra finesser. 4.4 Avkodare (Fairchild MM74C922N) För att knappsatsen skulle kunna prata med processorn användes en 4- bitars avkodare. Denna skannar igenom rader och kolonner på knappsatsen efter nedtryckta knappar och skickar sedan ut en 4-bitars signal till processorn som motsvarar siffran man tryckte ned. Avkodaren motverkar också bounces som uppkommer då knapparna hoppar till då man trycker ner dem och ger ostabila utsignaler, detta regleras med kondingar. 5 Resultat Skyltens huvudfunktion, att vid knapptryckning kunna visa nummer, fungerar väl och skulle kunna användas idag. Inmatning sker genom att mata in valfritt ett-till tresiffrigt tal med sifferknapparna 1-9. Efter inmatning måste talet bekräftas genom att trycka på F-knappen vartefter det valda talet skrivs ut på plattan. För att inte behöva skriva in varje tal vart för sig finns knapparna A och B tillgängliga som helt enkelt minskar respektive ökar det nuvarande talet. För de tillfällen då skylten inte behövs finns knappen C. 3
Då C trycks startas en bestämd sekvens av Conway s Game of Life, som dock än inte kan avslutas på grund av problem med interrupt hanteringen. 6 Diskussion 6.1 Problem Vi har stött på ganska många problem under kursens gång men dessa har vi (förhoppningsvis) löst och lärt oss något på. Vi läste datorteknik under tiden vi läste kursen och då det var ett rekommenderat förkunskapskrav så fick vi lära oss det viktigaste själva. Ingen av oss hade skrivit program i C tidigare, förutom ett par få rader. Detta gjorde att det tog lite längre tid att komma igång med projektet då vi var tvugna att lära oss kommunicera med processorn först. Inte nog med det, vi hade absolut ingen aning om hur flip-dot matrisen fungerade och det fanns dessutom inget datablad till denna. Mycket tid gick åt att göra mätningar för att mappa fram hur allt var kopplat. Det enda vi kunde få fram datablad på var de multiplexrar som satt på skylten, men dessa var ganska magiskt kopplade så det tog ett tag att klura ut deras funktion, detta löste vi också efter många timmars mätningar. Programmeringsmässigt lade vi ner närmre 50 timmar och merparten av dessa gick åt att felsöka, justera och inte minst optimera, då vi endast hade 1kB minne till förfogande. Det var väldigt svårt vid vissa tillfällen att veta vad som var fel, om det var koden eller hårdvaran, då vi aldrig riktigt visste vad som låg i processorns minne eller om hårdvaran bråkade. Vi har lärt oss väldigt mycket om minnesoptimering, något som man verkligen inte tänkte på i grundkurserna i programmering. Det sista problemet vi stötte på var när en av ingenjörerna, på något magiskt sätt, lyckades koppla fel och matade AVR:en med 20V, vilket den inte tyckte om och slutade då svara. Vi har löst detta med en spänningsregulator och lite andra diverse skydd mot dumheter. 6.2 Sammanfattning Allt som allt har kursen varit mycket intressant i avseende på hur mycket vi lärt oss. Till exempel har vi lärt oss hur C-programmering skiljer sig från java, vilket vi märkte vid hantering av matriser och vektorer. Kursen har även gett en djupare förståelse i hur man arbetar i grupp med ett större projekt och det slitsamma jobbet att felsöka. Om vi just ser på felsökningen har databladen som vi haft tillgängliga, främst genom kursens hemsida, varit obetalbara. Att vi precis hade läst digitalteknik när vi påbörjade kursen hjälpte också mycket då man i den kursens laborationer påträffade en hel del datablad. Datortekniken som vi läste under kursens gång hjälpte oss också mycket med projektet då huvuddelen av vårat program är baserat på 4
interrupts. Trots detta rekommenderar vi inte att läsa kursen under andra året. För hur givande det än är så är vi övertygade om att det är möjligt att lära sig långt mer i fjärde eller femte året. Kursen i sig rekommenderas starkt då den är intressant, rolig och mycket givande. A Kretsschema 5
B Källkod 1 #define F_CPU 1000000ul 2 #define sei() 3 #define cli() 4 5 #include <stdio.h> 6 #include <avr/interrupt.h> 7 #include <avr/io.h> 8 #include <util/delay.h> 9 #include <stdlib.h> 10 #include <stdbool.h> 11 12 // Declare subroutines 13 void set(int r, int c); 14 void unset(int r, int c); 15 void printnumber(char num, char offset); 16 void writenumber(char number[], char offset); 17 void gameoflife(); 18 void printesek(); 19 void clearmatrix(); 20 void incrnum(int a); 21 void decrnum(int a); 22 void fackit(); 23 void fackitclean(); 24 25 int num[3] = {-1, -1, -1}; // The vector is "cleared" 26 int nums[3] = {-1, -1, -1}; 27 int plats = 0; 28 _Bool gol = false; 29 30 const char row[] = {1,2,3,4,5,6,7,9,10,11,12,13,14,15,17,18}; 31 const char col[] = {1,2,3,4,5,6,7,9,10,11,12,13,14,15,17,18,19,20,21,22,23,25,26,27,28,29,30,31}; 32 33 34 int main(void) 35 { 36 // enables interrupt 1 at positive flanck 37 MCUCR = 0b00000011; 38 GICR = 0b01000000; 39 sei(); 40 41 // puts PORTA && PORTB as outputs 42 DDRA = 0xff; 43 DDRB = 0xff; 44 45 // clears the matrix at reset 46 clearmatrix(); 47 48 // DDRD = 0xff; 49 50 while(1) { 51 // the main function, wait for interrupts 52 if (gol == 1) { 6
53 //gameoflife(); 54 set(6, 6); 55 } 56 } 57 58 return 0; 59 60 } 61 62 // interrupt routine 63 ISR(INT0_vect) 64 { 65 char inputnum = PIND; // reads the input on PORTD 66 67 inputnum &= 0b01111000; // masks the input from the keyboard on P3-P6 68 inputnum = inputnum >> 3; // shifts down the number so it s coded correctly 69 70 if(!(inputnum < 10 && plats > 3)) { //kollar om man frsker trycka in fler [U+FFFD]tre siffro 71 switch (inputnum) { 72 case 0: 73 num[plats] = 0; 74 plats++; 75 break; 76 case 1: 77 num[plats] = 1; 78 plats++; 79 break; 80 case 2: 81 num[plats] = 2; 82 plats++; 83 break; 84 case 3: 85 num[plats] = 3; 86 plats++; 87 break; 88 case 4: 89 num[plats] = 4; 90 plats++; 91 break; 92 case 5: 93 num[plats] = 5; 94 plats++; 95 break; 96 case 6: 97 num[plats] = 6; 98 plats++; 99 break; 100 case 7: 101 num[plats] = 7; 102 plats++; 103 break; 104 case 8: 105 num[plats] = 8; 106 plats++; 7
107 break; 108 case 9: 109 num[plats] = 9; 110 plats++; 111 break; 112 case 10: // A, Minska numret som visas med 1 113 decrnum(2); 114 fackit(); 115 break; 116 case 11: // B, [U+FFFD]a numret som visas med 1 117 incrnum(2); 118 fackit(); 119 break; 120 case 12: // C, Kr game of life 121 gameoflife(); 122 break; 123 case 13: // D, 124 break; 125 case 14: // E, Skriver ut Esek 126 clearmatrix(); 127 printesek(); 128 break; 129 case 15: // F, Fack it 130 if (num[0] == 6 && num[1] == 6 && num[2] == 6) { // fun fact... 131 printesek(); 132 } else { 133 fackitclean(); 134 } 135 break; 136 } 137 } 138 } 139 140 void fackitclean() 141 { 142 clearmatrix(); 143 set(15, 27); 144 145 int numofnegs = 0; 146 for (int i = 0; i < 3; i++) { 147 if (num[i] < 0) { 148 numofnegs++; 149 } 150 } 151 152 if (numofnegs == 1) { // shifts the numbers to the right decimal position 153 num[2] = num[1]; 154 num[1] = num[0]; 155 num[0] = -1; 156 } 157 158 if (numofnegs == 2) { // shifts the number to the right decimal position 159 num[2] = num[0]; 160 num[0] = -1; 8
161 } 162 163 for (int i = 0; i < 3; i++) { 164 nums[i] = num[i]; 165 if (num[i] >= 0) { 166 printnumber(num[i], i * 9); 167 num[i] = -1; 168 } 169 } 170 171 plats = 0; // clears the position we are writing to in the vector 172 } 173 174 void fackit() 175 { 176 clearmatrix(); 177 set(15, 27); 178 179 for (int i = 0; i < 3; i++) { 180 if (nums[i] >= 0) { 181 printnumber(nums[i], i * 9); 182 } 183 } 184 185 plats = 0; 186 } 187 188 void incrnum(int pos) { 189 if(pos >= 0) { 190 nums[pos]++; 191 if(nums[pos] == 0) { 192 nums[pos]++; 193 } 194 if(nums[pos] == 10) { 195 nums[pos] = 0; 196 incrnum(pos-1); 197 } 198 } 199 } 200 201 void decrnum(int pos) 202 { 203 if(pos >= 0) { 204 if(nums[pos] <= -1) { 205 nums[0] = 9; 206 nums[1] = 9; 207 nums[2] = 9; 208 } else { 209 nums[pos]--; 210 if(nums[pos] == -1) { 211 nums[pos] = 9; 212 decrnum(pos-1); 213 } 214 } 9
215 } 216 } 217 218 void clearmatrix() 219 { 220 for (int i = 0; i < 16; i++) { 221 for (int j = 0; j < 28; j++) { 222 unset(i,j); 223 } 224 } 225 } 226 227 void gameoflife() 228 { 229 230 char matrix[16][28]; 231 char matrixcopy[16][28]; 232 233 // LUURINGAR!! 234 int countstep = 0; 235 int rndnum = num[2]; 236 237 // clears the matrices 238 for (int i = 0; i < 16; i++) { 239 for (int j = 0; j < 28; j++) { 240 matrix[i][j] = 0; 241 matrixcopy[i][j] = 0; 242 } 243 } 244 245 switch(rndnum) { 246 247 case 1: // r-pentomino 248 matrix[9][12] = 1; 249 matrix[8][12] = 1; 250 matrix[7][12] = 1; 251 matrix[8][11] = 1; 252 matrix[7][13] = 1; 253 break; 254 255 case 2: // circle 256 matrix[6][11] = 1; 257 matrix[6][12] = 1; 258 matrix[7][10] = 1; 259 matrix[8][10] = 1; 260 matrix[8][13] = 1; 261 matrix[9][11] = 1; 262 matrix[9][12] = 1; 263 break; 264 265 case 3: // r-pentomino + circle 266 matrix[9][12] = 1; 267 matrix[8][12] = 1; 268 matrix[7][12] = 1; 10
269 matrix[8][11] = 1; 270 matrix[7][13] = 1; 271 272 matrix[6][11] = 1; 273 matrix[6][12] = 1; 274 matrix[7][10] = 1; 275 matrix[8][10] = 1; 276 matrix[8][13] = 1; 277 matrix[9][11] = 1; 278 matrix[9][12] = 1; 279 break; 280 281 default: 282 break; 283 } 284 285 // views the bottom of the food chain 286 for (int i = 0; i < 16; i++) { 287 for (int j = 0; j < 28; j++) { 288 if (matrix[i][j] == 1) { 289 set(i, j); 290 } else { 291 unset(i, j); 292 } 293 } 294 } 295 296 _delay_ms(20); 297 298 //sei(); // enable interrupts inside the GoL interrupt 299 _Bool gols = gol; 300 while(!gols) { // MAIN!!!!! 301 302 set(2, 2); 303 int neighbours = 0; 304 305 for (int i = 0; i < 16; i++) { // choose pixel 306 for (int j = 0; j < 28; j++) { 307 for (int x = i - 1; x <= i + 1; x++) { // checks thee neighbours 308 for (int y = j - 1; y <= j + 1; y++) { 309 if (!(x < 0 x > 15 y < 0 y > 27)) { 310 if (matrix[x][y] == 1) { 311 neighbours++; 312 } 313 } 314 } 315 } 316 317 if (matrix[i][j] == 1) { // excluding yourself, if alive 318 neighbours--; 319 } 320 321 // Evolution, as it was ment to be 322 if (matrix[i][j] == 1 && (neighbours <= 3 neighbours >= 2)) { 11
323 matrixcopy[i][j] = 1; 324 } 325 326 if (matrix[i][j] == 1 && (neighbours < 2 neighbours > 3)) { 327 matrixcopy[i][j] = 0; 328 } 329 330 if (matrix[i][j] == 0 && neighbours == 3) { 331 matrixcopy[i][j] = 1; 332 } 333 334 neighbours = 0; 335 } 336 } 337 338 // LUUUUUURING!! 339 if (rndnum == 3 && countstep == 20) { // glider 340 matrixcopy[1][2] = 1; 341 matrixcopy[2][3] = 1; 342 matrixcopy[3][1] = 1; 343 matrixcopy[3][2] = 1; 344 matrixcopy[3][3] = 1; 345 346 countstep = 0; 347 } 348 349 // prints the current state in the food chain 350 for (int i = 0; i < 16; i++) { 351 for (int j = 0; j < 28; j++) { 352 if (matrixcopy[i][j] == 1) { 353 set(i, j); 354 } 355 if (matrixcopy[i][j] == 0) { 356 unset(i, j); 357 } 358 } 359 } 360 361 for (int i = 0; i < 16; i++) { 362 for (int j = 0; j < 28; j++) { 363 matrix[i][j] = matrixcopy[i][j]; // updates the matrix with the current state 364 } 365 } 366 367 countstep++; // counter to gliderz 368 369 } // end of while 370 371 clearmatrix(); 372 } 373 374 375 376 void printnumber(char num, char offset) 12
377 { 378 // declaring how the numbers should be printed 379 char zero[16] = {0x00, 0x3C, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E, 380 char one[16] = {0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 381 char two[16] = {0x00, 0x3C, 0x7E, 0xC3, 0x83, 0x03, 0x03, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 382 char three[16] = {0x00, 0x7C, 0xFE, 0x03, 0x03, 0x03, 0x03, 0x3E, 0x3E, 0x03, 0x03, 0x03, 0x03, 0xFE 383 char four[16] = {0x00, 0x06, 0x0E, 0x1E, 0x36, 0x66, 0xC6, 0xFF, 0xFF, 0x06, 0x06, 0x06, 0x06, 0x06, 384 char five[16] = {0x00, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xFc, 0xFE, 0x03, 0x03, 0x03, 0x03, 0xFE, 385 char six[16] = {0x00, 0xFF, 0xFF, 0xC3, 0xC3, 0xC0, 0xC0, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 386 char seven[16] = {0x00, 0xFF, 0xFF, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 387 char eight[16] = {0x00, 0x7E, 0xFF, 0xC3, 0xC3, 0xC3, 0x7E, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF 388 char nine[16] = {0x00, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 389 390 switch(num) { 391 392 case 0: 393 writenumber(zero,offset); 394 break; 395 case 1: 396 writenumber(one,offset); 397 break; 398 case 2: 399 writenumber(two,offset); 400 break; 401 case 3: 402 writenumber(three,offset); 403 break; 404 case 4: 405 writenumber(four,offset); 406 break; 407 case 5: 408 writenumber(five,offset); 409 break; 410 case 6: 411 writenumber(six,offset); 412 break; 413 case 7: 414 writenumber(seven,offset); 415 break; 416 case 8: 417 writenumber(eight,offset); 418 break; 419 case 9: 420 writenumber(nine,offset); 421 break; 422 } 423 424 425 } //end of setnum 426 427 void writenumber(char number[16], char offset) 428 { 429 for(int i = 0; i < 16; i++) { 430 for(int k = 0; k < 8; k++) { 13
431 if((number[i] << k & 0b10000000) == 128) { 432 set(i, k + offset); 433 } 434 } 435 } 436 } 437 438 void printesek() 439 { 440 clearmatrix(); 441 442 char esek[16][28] = { 443 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 444 {0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 445 {0,0,0,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0}, 446 {0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0}, 447 {0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0}, 448 {0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0}, 449 {0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0}, 450 {0,1,1,1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0}, 451 {0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0}, 452 {0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,1,0,0,0,0,0}, 453 {0,0,0,1,1,0,0,0,0,1,1,1,1,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0}, 454 {0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,0,0,1,0,0,0}, 455 {0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0}, 456 {0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,1,0}, 457 {0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0}, 458 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 459 }; 460 461 for (int i = 0; i < 16; i++) { 462 for (int j = 0; j < 28; j++) { 463 if (esek[i][j] == 1) { 464 set(i, j); 465 } 466 } 467 } 468 469 } 470 471 472 void set(int r, int c) //s[u+fffd]er pixlar 473 { 474 PORTB = row[r] + 64; 475 PORTA = col[c] + 64; 476 _delay_ms(2); 477 PORTA = col[c]; 478 } 479 480 void unset(int r, int c) //st[u+fffd]er av pixlar 481 { 482 PORTB = row[r] + 32; 483 PORTA = col[c] + 96; 484 _delay_ms(2); 14
485 PORTA = col[c] + 32; 486 } 15