Merge pull request #1 from schuay/master
[simavr] / examples / board_timer_64led / timer_64led.c
1 /*
2         timer_64led.c
3         
4         Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <libgen.h>
25 #if __APPLE__
26 #include <GLUT/glut.h>
27 #else
28 #include <GL/glut.h>
29 #endif
30 #include <pthread.h>
31
32 #include "sim_avr.h"
33 #include "avr_ioport.h"
34 #include "avr_spi.h"
35 #include "avr_timer.h"
36 #include "sim_elf.h"
37 #include "sim_gdb.h"
38 #include "sim_vcd_file.h"
39
40 #include "button.h"
41 #include "hc595.h"
42
43 enum {
44         B_START = 0, B_STOP, B_RESET,
45         B_MAX
46 };
47 button_t button[B_MAX]; // Start/Stop/Reset
48 volatile int do_button_press[B_MAX] = {0};
49 avr_t * avr = NULL;
50 avr_vcd_t vcd_file;
51 hc595_t shifter;
52
53 int display_flag = 0;
54 volatile uint32_t       display_bits = 0;
55 volatile uint8_t        display_pwm = 0;
56
57 float pixsize = 16;
58 int window;
59
60 /*
61  * called when the AVR has latched the 595
62  */
63 void hc595_changed_hook(struct avr_irq_t * irq, uint32_t value, void * param)
64 {
65         display_bits = value;
66         display_flag++;
67 }
68
69 /*
70  * called when the AVR has changed the display brightness
71  */
72 void pwm_changed_hook(struct avr_irq_t * irq, uint32_t value, void * param)
73 {
74         display_pwm = value;
75         display_flag++;
76 }
77
78 void displayCB(void)            /* function called whenever redisplay needed */
79 {
80         // OpenGL rendering goes here...
81         glClear(GL_COLOR_BUFFER_BIT);
82
83         // Set up modelview matrix
84         glMatrixMode(GL_MODELVIEW); // Select modelview matrix
85         glLoadIdentity(); // Start with an identity matrix
86
87         float grid = pixsize;
88         float size = grid * 0.8;
89         float color_on = (float)(0xff - display_pwm) / 15.0f;
90         float color_off = 0.1;
91         if (color_on < color_off)
92                 color_on = color_off;
93
94         glTranslatef(pixsize / 2.25f, pixsize / 1.8f, 0);
95         
96     glBegin(GL_QUADS);
97         
98         for (int di = 0; di < 4; di++) {
99                 uint8_t digit = display_bits >> (di * 8);
100                 
101                 for (int i = 0; i < 8; i++) {   
102                         glColor3f(0,0, digit & (1 << i) ? color_on : color_off);
103                         float dx = ((di * 5.5)) * pixsize, dy = 0*pixsize;
104                         switch (i) {
105                                 case 3:
106                                         dy += 3.0f * pixsize;
107                                 case 6:
108                                         dy += 3.0f * pixsize;
109                                 case 0:
110                                         dx += 1.0f * pixsize;
111                                         glVertex2f(dx + size, dy + size); glVertex2f(dx, dy + size); glVertex2f(dx, dy); glVertex2f(dx + size, dy);
112                                         dx += 1.0f * pixsize;
113                                         glVertex2f(dx + size, dy + size); glVertex2f(dx, dy + size); glVertex2f(dx, dy); glVertex2f(dx + size, dy);
114                                         break;
115                                 case 7: // dot!
116                                         dx += 4.25 * pixsize;
117                                         switch (di) {
118                                                 case 0:
119                                                 case 3:
120                                                         dy += 6.25 * pixsize;
121                                                         break;
122                                                 case 1:
123                                                         dy += 2 * pixsize;
124                                                         break;
125                                                 case 2:
126                                                         dy += 4 * pixsize;
127                                                         dx -= 5.50 * pixsize;
128                                                         break;
129                                         }
130                                                         
131                                         glVertex2f(dx + size, dy + size); glVertex2f(dx, dy + size); glVertex2f(dx, dy); glVertex2f(dx + size, dy);                                     
132                                         break;
133                                 default:
134                                         if (i == 1 || i == 2)
135                                                 dx += 3.0f * pixsize;
136                                         if (i == 4 || i == 2)
137                                                 dy += 4.0f * pixsize;
138                                         else
139                                                 dy += 1.0f * pixsize;                                   
140                                         glVertex2f(dx + size, dy + size); glVertex2f(dx, dy + size); glVertex2f(dx, dy); glVertex2f(dx + size, dy);
141                                         dy += 1.0f * pixsize;
142                                         glVertex2f(dx + size, dy + size); glVertex2f(dx, dy + size); glVertex2f(dx, dy); glVertex2f(dx + size, dy);
143                                         break;
144                         }
145                 }
146         }
147
148     glEnd();
149     glutSwapBuffers();
150     //glFlush();                                /* Complete any pending operations */
151 }
152
153 void keyCB(unsigned char key, int x, int y)     /* called on key press */
154 {
155         if (key == 'q')
156                 exit(0);
157         //static uint8_t buf[64];
158         switch (key) {
159                 case 'q':
160                 case 0x1f: // escape
161                         exit(0);
162                         break;
163                 case '1' ... '3':
164                         printf("Press %d\n", key-'1');
165                         do_button_press[key-'1']++; // pass the message to the AVR thread
166                         break;
167                 case 'r':
168                         printf("Starting VCD trace\n");
169                         avr_vcd_start(&vcd_file);
170                         break;
171                 case 's':
172                         printf("Stopping VCD trace\n");
173                         avr_vcd_stop(&vcd_file);
174                         break;
175         }
176 }
177
178 // gl timer. if the pin have changed states, refresh display
179 void timerCB(int i)
180 {
181         static int oldstate = -1;
182         // restart timer
183         glutTimerFunc(1000/64, timerCB, 0);
184
185         if (oldstate != display_flag) {
186                 oldstate = display_flag;
187                 glutPostRedisplay();
188         }
189 }
190
191 static void * avr_run_thread(void * ignore)
192 {
193         int b_press[3] = {0};
194         
195         while (1) {
196                 avr_run(avr);
197
198                 for (int i = 0; i < 3; i++) {
199                         if (do_button_press[i] != b_press[i]) {
200                                 b_press[i] = do_button_press[i];
201                                 printf("Button pressed %d\n", i);
202                                 button_press(&button[i], 100000);
203                         }
204                 }
205         }
206         return NULL;
207 }
208
209
210 int main(int argc, char *argv[])
211 {
212         elf_firmware_t f;
213         const char * fname =  "atmega168_timer_64led.axf";
214         //char path[256];
215
216 //      sprintf(path, "%s/%s", dirname(argv[0]), fname);
217         //printf("Firmware pathname is %s\n", path);
218         elf_read_firmware(fname, &f);
219
220         printf("firmware %s f=%d mmcu=%s\n", fname, (int)f.frequency, f.mmcu);
221
222         avr = avr_make_mcu_by_name(f.mmcu);
223         if (!avr) {
224                 fprintf(stderr, "%s: AVR '%s' now known\n", argv[0], f.mmcu);
225                 exit(1);
226         }
227         avr_init(avr);
228         avr_load_firmware(avr, &f);
229
230         //
231         // initialize our 'peripherals'
232         //
233         hc595_init(avr, &shifter);
234         
235         button_init(avr, &button[B_START], "button.start");
236         avr_connect_irq(
237                 button[B_START].irq + IRQ_BUTTON_OUT,
238                 avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('C'), 0));
239         button_init(avr, &button[B_STOP], "button.stop");
240         avr_connect_irq(
241                 button[B_STOP].irq + IRQ_BUTTON_OUT,
242                 avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 1));
243         button_init(avr, &button[B_RESET], "button.reset");
244         avr_connect_irq(
245                 button[B_RESET].irq + IRQ_BUTTON_OUT,
246                 avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 0));
247
248         // connects the fake 74HC595 array to the pins
249         avr_irq_t * i_mosi = avr_io_getirq(avr, AVR_IOCTL_SPI_GETIRQ(0), SPI_IRQ_OUTPUT),
250                         * i_reset = avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('D'), 4),
251                         * i_latch = avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('D'), 7);
252         avr_connect_irq(i_mosi, shifter.irq + IRQ_HC595_SPI_BYTE_IN);
253         avr_connect_irq(i_reset, shifter.irq + IRQ_HC595_IN_RESET);
254         avr_connect_irq(i_latch, shifter.irq + IRQ_HC595_IN_LATCH);
255
256         avr_irq_t * i_pwm = avr_io_getirq(avr, AVR_IOCTL_TIMER_GETIRQ('0'), TIMER_IRQ_OUT_PWM0);
257         avr_irq_register_notify(
258                 i_pwm,
259                 pwm_changed_hook, 
260                 NULL);  
261         avr_irq_register_notify(
262                 shifter.irq + IRQ_HC595_OUT,
263                 hc595_changed_hook, 
264                 NULL);
265
266         // even if not setup at startup, activate gdb if crashing
267         avr->gdb_port = 1234;
268         if (0) {
269                 //avr->state = cpu_Stopped;
270                 avr_gdb_init(avr);
271         }
272
273         /*
274          *      VCD file initialization
275          *      
276          *      This will allow you to create a "wave" file and display it in gtkwave
277          *      Pressing "r" and "s" during the demo will start and stop recording
278          *      the pin changes
279          */
280         avr_vcd_init(avr, "gtkwave_output.vcd", &vcd_file, 10000 /* usec */);
281
282         avr_vcd_add_signal(&vcd_file, 
283                 avr_get_interrupt_irq(avr, 7), 1 /* bit */ ,
284                 "TIMER2_COMPA" );
285         avr_vcd_add_signal(&vcd_file, 
286                 avr_get_interrupt_irq(avr, 17), 1 /* bit */ ,
287                 "SPI_INT" );
288         avr_vcd_add_signal(&vcd_file, 
289                 i_mosi, 8 /* bits */ ,
290                 "MOSI" );
291
292         avr_vcd_add_signal(&vcd_file, 
293                 i_reset, 1 /* bit */ ,
294                 "595_RESET" );
295         avr_vcd_add_signal(&vcd_file, 
296                 i_latch, 1 /* bit */ ,
297                 "595_LATCH" );
298         avr_vcd_add_signal(&vcd_file, 
299                 button[B_START].irq + IRQ_BUTTON_OUT, 1 /* bits */ ,
300                 "start" );
301         avr_vcd_add_signal(&vcd_file, 
302                 button[B_STOP].irq + IRQ_BUTTON_OUT, 1 /* bits */ ,
303                 "stop" );
304         avr_vcd_add_signal(&vcd_file, 
305                 button[B_RESET].irq + IRQ_BUTTON_OUT, 1 /* bits */ ,
306                 "reset" );
307
308         avr_vcd_add_signal(&vcd_file, 
309                 shifter.irq + IRQ_HC595_OUT, 32 /* bits */ ,
310                 "HC595" );
311         avr_vcd_add_signal(&vcd_file, 
312                 i_pwm, 8 /* bits */ ,
313                 "PWM" );
314
315         // 'raise' it, it's a "pullup"
316         avr_raise_irq(button[B_START].irq + IRQ_BUTTON_OUT, 1);
317         avr_raise_irq(button[B_STOP].irq + IRQ_BUTTON_OUT, 1);
318         avr_raise_irq(button[B_RESET].irq + IRQ_BUTTON_OUT, 1);
319
320         printf( "Demo : This is a real world firmware, a 'stopwatch'\n"
321                         "   timer that can count up to 99 days. It features a PWM control of the\n"
322                         "   brightness, blinks the dots, displays the number of days spent and so on.\n\n"
323                         "   Press '0' to press the 'start' button\n"
324                         "   Press '1' to press the 'stop' button\n"
325                         "   Press '2' to press the 'reset' button\n"
326                         "   Press 'q' to quit\n\n"
327                         "   Press 'r' to start recording a 'wave' file - with a LOT of data\n"
328                         "   Press 's' to stop recording\n"
329                         "  + Make sure to watch the brightness dim once you stop the timer\n\n"
330                         );
331
332         /*
333          * OpenGL init, can be ignored
334          */
335         glutInit(&argc, argv);          /* initialize GLUT system */
336
337
338         int w = 22, h = 8;
339         
340         glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
341         glutInitWindowSize(w * pixsize, h * pixsize);           /* width=400pixels height=500pixels */
342         window = glutCreateWindow("Press 0, 1, 2 or q");        /* create window */
343
344         // Set up projection matrix
345         glMatrixMode(GL_PROJECTION); // Select projection matrix
346         glLoadIdentity(); // Start with an identity matrix
347         glOrtho(0, w * pixsize, 0, h * pixsize, 0, 10);
348         glScalef(1,-1,1);
349         glTranslatef(0, -1 * h * pixsize, 0);
350
351         glutDisplayFunc(displayCB);             /* set window's display callback */
352         glutKeyboardFunc(keyCB);                /* set window's key callback */
353         glutTimerFunc(1000 / 24, timerCB, 0);
354
355         // the AVR run on it's own thread. it even allows for debugging!
356         pthread_t run;
357         pthread_create(&run, NULL, avr_run_thread, NULL);
358
359         glutMainLoop();
360 }