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