Add menu to browser the file on sd.
[bertos.git] / boards / sam3x-ek / examples / sam3x-ek_codec / main.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2011 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief Atmel SAM3X-EK testcase
34  *
35  * \author Stefano Fedrigo <aleph@develer.com>
36  */
37
38 #include "bitmaps.h"
39
40 #include "hw/hw_led.h"
41 #include "hw/hw_lcd.h"
42 #include "hw/hw_adc.h"
43 #include "hw/hw_sdram.h"
44 #include "hw/hw_sd.h"
45
46 #include "cfg/cfg_i2s.h"
47
48 #include <cfg/debug.h>
49
50 #include <cpu/irq.h>
51 #include <cpu/irq.h>
52 #include <cpu/byteorder.h>
53
54 #include <drv/kbd.h>
55 #include <drv/i2c.h>
56 #include <drv/i2s.h>
57 #include <drv/sd.h>
58 #include <drv/timer.h>
59 #include <drv/lcd_hx8347.h>
60 #include <drv/adc.h>
61 #include <drv/wm8731.h>
62 #include <drv/dmac_sam3.h>
63 #include <drv/sd.h>
64
65 #include <gfx/gfx.h>
66 #include <gfx/font.h>
67 #include <gfx/text.h>
68 #include <gui/menu.h>
69 #include <icons/logo.h>
70
71 #include <io/kfile.h>
72 #include <io/kfile_block.h>
73
74 #include <kern/signal.h>
75 #include <kern/proc.h>
76
77 #include <fs/fat.h>
78
79 #include <struct/list.h>
80
81 #include <verstag.h>
82 #include <buildrev.h>
83
84 #include <string.h>
85
86 // Keyboard
87 #define KEY_MASK (K_LEFT | K_RIGHT)
88
89 // Kernel
90 #define PROC_STACK_SIZE KERN_MINSTACKSIZE * 2
91
92 static PROC_DEFINE_STACK(hp_stack, PROC_STACK_SIZE);
93 static PROC_DEFINE_STACK(lp_stack, PROC_STACK_SIZE);
94
95 /*
96  * Codec has 7-bit address, the eighth is the R/W bit, so we
97  * write the codec address with one bit shifted left
98  */
99 #define CODEC_ADDR 0x36
100
101
102
103 typedef struct WavHdr
104 {
105         char chunk_id[4];
106         uint32_t chunk_size;
107         char format[4];
108
109         char subchunk1_id[4];
110         uint32_t subchunk1_size;
111         uint16_t audio_format;
112         uint16_t num_channels;
113         uint32_t sample_rate;
114         uint32_t byte_rate;
115         uint16_t block_align;
116         uint16_t bits_per_sample;
117
118         uint8_t subchunk2[8];
119 } WavHdr;
120
121
122 static uint8_t raster[RAST_SIZE(LCD_WIDTH, LCD_HEIGHT)];
123 static Bitmap lcd_bitmap;
124 extern Font font_gohu;
125 static int lcd_brightness = LCD_BACKLIGHT_MAX;
126 static Process *hp_proc, *lp_proc;
127 static hptime_t start, end;
128
129
130 uint8_t tmp[4096];
131
132 // SD fat filesystem context
133 FATFS fs;
134 FatFile log_file;
135 FatFile acq_file;
136
137 static I2c i2c;
138 static I2s i2s;
139 static Wm8731 wm8731_ctx;
140
141
142 static void screen_saver(Bitmap *bm)
143 {
144         int x1, y1, x2, y2;
145         int i;
146
147         for (i = 0; ; i++)
148         {
149                 x1 = i % LCD_WIDTH;
150                 y1 = i % LCD_HEIGHT;
151
152                 x2 = LCD_WIDTH - i % LCD_WIDTH;
153                 y2 = LCD_HEIGHT - i % LCD_HEIGHT;
154
155                 gfx_bitmapClear(bm);
156                 gfx_rectDraw(bm, x1, y1, x2, y2);
157                 lcd_hx8347_blitBitmap(bm);
158                 if (kbd_peek() & KEY_MASK)
159                         break;
160         }
161 }
162
163
164 INLINE hptime_t get_hp_ticks(void)
165 {
166         return (timer_clock_unlocked() * TIMER_HW_CNT) + timer_hw_hpread();
167 }
168
169 static void NORETURN hp_process(void)
170 {
171         while (1)
172         {
173                 sig_wait(SIG_USER0);
174                 end = get_hp_ticks();
175                 timer_delay(100);
176                 sig_send(lp_proc, SIG_USER0);
177         }
178 }
179
180 static void NORETURN lp_process(void)
181 {
182         while (1)
183         {
184                 start = get_hp_ticks();
185                 sig_send(hp_proc, SIG_USER0);
186                 sig_wait(SIG_USER0);
187         }
188 }
189
190 /*
191  * Lcd
192  */
193 static void setBrightness(Bitmap *bm)
194 {
195         while (1)
196         {
197                 gfx_bitmapClear(bm);
198                 text_xprintf(bm, 1, 0, TEXT_FILL | TEXT_CENTER, "Brightness: %d", lcd_brightness);
199                 text_xprintf(bm, 3, 0, TEXT_FILL | TEXT_CENTER, "RIGHT key: change");
200                 text_xprintf(bm, 4, 0, TEXT_FILL | TEXT_CENTER, "LEFT  key: back  ");
201                 lcd_hx8347_blitBitmap(bm);
202
203                 keymask_t mask = kbd_get();
204
205                 if (mask & K_LEFT)
206                         break;
207                 else if (mask & K_RIGHT)
208                 {
209                         if (++lcd_brightness > LCD_BACKLIGHT_MAX)
210                                 lcd_brightness = 0;
211                         lcd_setBacklight(lcd_brightness);
212                 }
213         }
214 }
215
216
217 static void NORETURN soft_reset(Bitmap * bm)
218 {
219         int i;
220
221         gfx_bitmapClear(bm);
222         for (i = 5; i; --i)
223         {
224                 text_xprintf(bm, 2, 0, TEXT_FILL | TEXT_CENTER, "%d", i);
225                 lcd_hx8347_blitBitmap(bm);
226                 timer_delay(1000);
227         }
228         text_xprintf(bm, 2, 0, TEXT_FILL | TEXT_CENTER, "REBOOT");
229         lcd_hx8347_blitBitmap(bm);
230         timer_delay(1000);
231
232         /* Perform a software reset request */
233         HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ;
234         UNREACHABLE();
235 }
236
237 static void read_adc(Bitmap *bm)
238 {
239         gfx_bitmapClear(bm);
240         text_xprintf(bm, 0, 0, TEXT_FILL | TEXT_CENTER, "ADC Value");
241         while (1)
242         {
243                 uint16_t value = ADC_RANGECONV(adc_read(1), 0, 3300);
244                 uint16_t temp = hw_convertToDegree (adc_read(ADC_TEMPERATURE_CH));
245
246                 text_xprintf(&lcd_bitmap, 2, 0, TEXT_FILL | TEXT_CENTER,
247                                                                         "Voltage on VR1: %d.%dV", value / 1000, value % 1000);
248                 text_xprintf(&lcd_bitmap, 3, 0, TEXT_FILL | TEXT_CENTER,
249                                                                         "CPU temperature: %d.%dC", temp / 10, temp % 10);
250                 lcd_hx8347_blitBitmap(bm);
251                 timer_delay(400);
252                 if (kbd_peek() & KEY_MASK)
253                         break;
254         }
255 }
256 #define FILE_NAME  "outfile.wav"
257 #define ACQ_FILE_NAME  FILE_NAME
258
259
260 uint8_t tmp[4096];
261
262 // SD fat filesystem context
263 FATFS fs;
264 FatFile log_file;
265 FatFile acq_file;
266
267
268
269 static int wav_check(KFile *fd)
270 {
271
272         WavHdr header;
273
274         if (kfile_read(fd, &header, sizeof(header)) != sizeof(header))
275         {
276                 kputs("Error reading wave file header\n");
277                 return EOF;
278         }
279
280         if (strncmp(header.chunk_id, "RIFF", 4))
281         {
282                 kputs("RIFF tag not found\n");
283                 goto error;
284         }
285
286         if (strncmp(header.format, "WAVE", 4))
287         {
288                 kputs("WAVE tag not found\n");
289                 goto error;
290         }
291
292         if (le16_to_cpu(header.audio_format) != 1)
293         {
294                 kprintf("Audio format not valid, found [%d]\n", le16_to_cpu(header.audio_format));
295                 goto error;
296         }
297
298         if (le16_to_cpu(header.num_channels) != 2)
299         {
300                 kprintf("Channels number not valid, found [%d]\n", le16_to_cpu(header.num_channels));
301                 goto error;
302         }
303
304
305         if (le32_to_cpu(header.sample_rate) != 44100)
306         {
307                 kprintf("Sample rate not valid, found [%ld]\n", le32_to_cpu(header.sample_rate));
308                 goto error;
309         }
310
311         if (le16_to_cpu(header.bits_per_sample) != 16)
312         {
313                 kprintf("Bits per sample not valid, found [%d]\n", le16_to_cpu(header.bits_per_sample));
314                 goto error;
315         }
316         return 0;
317
318 error:
319         return 1;
320 }
321
322
323 static int wav_writeHdr(KFile *fd, uint16_t rate, uint16_t channels, uint16_t bits)
324 {
325         WavHdr header;
326
327         memcpy(&header.chunk_id, "RIFF", 4);
328         memcpy(&header.format, "WAVE", 4);
329         header.audio_format = cpu_to_le16((uint16_t)1);
330         header.num_channels = cpu_to_le16(channels);
331         header.sample_rate = cpu_to_le16(rate);
332         header.bits_per_sample = cpu_to_le16(bits);
333
334         kfile_seek(fd, 0, KSM_SEEK_SET);
335         kfile_write(fd, &header, sizeof(header));
336
337         return 0;
338 }
339
340 size_t count = 0;
341 static void codec_play(struct I2s *i2s, void *_buf, size_t len)
342 {
343         count += kfile_read(&log_file.fd, _buf, len);
344         if (count >= log_file.fat_file.fsize - sizeof(WavHdr))
345         {
346                 kprintf("stop %d\n", count);
347                 i2s_dmaTxStop(i2s);
348                 count = 0;
349         }
350 }
351
352 static void codec_rec(struct I2s *i2s, void *_buf, size_t len)
353 {
354         count += kfile_write(&acq_file.fd, _buf, len);
355         if (count >= 1024 * 1024)
356         {
357                 kprintf("stop %d\n", count);
358                 i2s_dmaRxStop(i2s);
359                 count = 0;
360         }
361 }
362
363
364
365 #define MAX_ITEM_NODES    30
366 #define MAX_ITEMS_ROW     15
367 #define NEXT_ITEM_COL     10
368
369 typedef struct FileItemNode
370 {
371         Node n;
372         char file_name[13];
373 } FileItemNode;
374
375 FileItemNode item_nodes[MAX_ITEM_NODES];
376 static Sd sd;
377
378 static void wav_play(Bitmap *bm, char *file_name)
379 {
380         gfx_bitmapClear(bm);
381
382         kprintf("Mount FAT filesystem.\n");
383         FRESULT result = f_mount(0, &fs);
384         bool sd_ok = true;
385         if (result != FR_OK)
386         {
387                 kprintf("Mounting FAT volumes error[%d]\n", result);
388                 sd_ok = false;
389         }
390
391         if (sd_ok)
392         {
393                 result = fatfile_open(&log_file, file_name,  FA_OPEN_EXISTING | FA_READ);
394                 if (result == FR_OK)
395                 {
396                         text_xprintf(bm, 1, 0, TEXT_CENTER, "Play wav file: %s", file_name);
397                         text_xprintf(bm, 2, 0, TEXT_CENTER, "File: %ld", log_file.fat_file.fsize);
398                         text_xprintf(bm, 3, 0, TEXT_CENTER, "Volume level %ld", ADC_RANGECONV(adc_read(1), 0, 100));
399                         kprintf("Open file: %s\n", file_name);
400                         wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, ADC_RANGECONV(adc_read(1), 0, 100));
401
402                         lcd_hx8347_blitBitmap(bm);
403                         if (wav_check(&log_file.fd) >= 0)
404                         {
405                                 kputs("Wav file play..\n");
406                                 i2s_dmaStartTxStreaming(&i2s, tmp, sizeof(tmp), sizeof(tmp) / 4, codec_play);
407                         }
408
409                         wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, 0);
410
411                         // Flush data and close the files.
412                         kfile_flush(&log_file.fd);
413                         kfile_close(&log_file.fd);
414                 }
415                 else
416                 {
417                         kprintf("Unable to open file: '%s' error[%d]\n", FILE_NAME, result);
418                 }
419         }
420         f_mount(0, NULL);
421
422         lcd_hx8347_blitBitmap(bm);
423 }
424
425
426 INLINE FileItemNode *refresh_cursor(Bitmap *bm, List *file_list, int select_idx)
427 {
428         FileItemNode *item;
429         FileItemNode *select_node;
430         int col = 0;
431         int row = 0;
432
433         gfx_bitmapClear(bm);
434         select_node = (FileItemNode *)LIST_HEAD(file_list);
435     FOREACH_NODE(item, file_list)
436         {
437                 if (row > MAX_ITEMS_ROW)
438                 {
439                         row = 1;
440                         col = NEXT_ITEM_COL;
441                 }
442
443                 text_style(bm, 0, STYLEF_MASK);
444                 if (select_idx <= MAX_ITEMS_ROW)
445                 {
446                         if (row == select_idx && col == 0)
447                                 text_style(bm, STYLEF_INVERT, STYLEF_INVERT);
448                                 select_node = item;
449                 }
450                 else
451                 {
452                         if (row == (select_idx - MAX_ITEMS_ROW) && col == NEXT_ITEM_COL)
453                                 text_style(bm, STYLEF_INVERT, STYLEF_INVERT);
454                                 select_node = item;
455                 }
456
457                 text_xprintf(bm, row, col, TEXT_NORMAL, "%s", item->file_name);
458                 row++;
459         }
460
461         lcd_hx8347_blitBitmap(bm);
462
463         return select_node;
464 }
465
466 static void sd_explorer(Bitmap *bm)
467 {
468         List file_list;
469         LIST_INIT(&file_list);
470         int file_list_size = 0;
471         gfx_bitmapClear(bm);
472
473         memcpy (&item_nodes[0].file_name, "<- Return..", sizeof(item_nodes[0].file_name));
474         ADDTAIL(&file_list, &item_nodes[0].n);
475
476         if (SD_CARD_PRESENT())
477         {
478                 timer_delay(10);
479
480                 bool sd_ok = sd_init(&sd, NULL, 0);
481                 FRESULT result;
482
483                 if (sd_ok)
484                 {
485                         kprintf("Mount FAT filesystem.\n");
486
487                         result = f_mount(0, &fs);
488                         if (result != FR_OK)
489                         {
490                                 kprintf("Mounting FAT volumes error[%d]\n", result);
491                                 sd_ok = false;
492                         }
493
494                         if (sd_ok)
495                         {
496                                 FILINFO fno;
497                                 DIR dir;
498
499                                 kputs("open dir\n");
500                                 /* Open the directory */
501                                 result = f_opendir(&dir, "/");
502                                 if (result == FR_OK)
503                                 {
504                                         // First element is reserved for "return" label
505                                         for (int i = 1;; i++)
506                                         {
507                                                 /* Read a directory item */
508                                                 result = f_readdir(&dir, &fno);
509                                                 if (result != FR_OK || fno.fname[0] == 0)
510                                                         break;  /* Break on error or end of dir */
511                                                 if (fno.fname[0] == '.')
512                                                         continue; /* Ignore dot entry */
513
514                                                 if (fno.fattrib & AM_DIR)
515                                                         continue;
516                                                 else
517                                                 {
518                                                         if (i < MAX_ITEM_NODES)
519                                                         {
520                                                                 memcpy (&item_nodes[i].file_name, fno.fname, sizeof(item_nodes[i].file_name));
521                                                                 ADDTAIL(&file_list, &item_nodes[i].n);
522                                                                 file_list_size++;
523                                                         }
524                                                         else
525                                                         {
526                                                                 kputs("No enought spase to show file list..\n");
527                                                                 break;
528                                                         }
529                                                 }
530                                         }
531                                 }
532                         }
533                         f_mount(0, NULL);
534                         kprintf("Umount\n");
535                 }
536         }
537         else
538         {
539                 kputs("No card insert..\n");
540                 text_xprintf(bm, 5, 0, TEXT_CENTER | TEXT_FILL, "%s", "No card insert..");
541         }
542
543         int idx = 0;
544         FileItemNode *selected_node = refresh_cursor(bm, &file_list, idx);
545         while (1)
546         {
547                 keymask_t key = kbd_peek();
548                 if (key & K_LEFT)
549                 {
550                         idx++;
551                         if (idx > file_list_size)
552                                 idx = 0;
553
554                         selected_node = refresh_cursor(bm, &file_list, idx);
555                         kprintf("lidx[%d]\n", idx);
556                 }
557                 if (key & K_RIGHT)
558                 {
559                         kprintf("ridx[%d]\n", idx);
560                         wav_play(bm, selected_node->file_name);
561                         if (idx == 0)
562                                 break;
563                 }
564         }
565 }
566
567 static void test_draw(Bitmap *bm)
568 {
569         gfx_bitmapClear(bm);
570         text_xprintf(bm, 0, 0, TEXT_NORMAL, "%s\n", "12345678.123");
571         text_xprintf(bm, 0, 10, TEXT_NORMAL, "%s\n", "12345678.123");
572         text_xprintf(bm, 1, 0, TEXT_NORMAL, "%s\n", "12345678.123");
573         text_xprintf(bm, 2, 0, TEXT_NORMAL, "%s\n", "12345678.123");
574         text_xprintf(bm, 3, 0, TEXT_NORMAL, "%s\n", "12345678.123");
575         lcd_hx8347_blitBitmap(bm);
576
577         timer_delay(400);
578         while (1)
579                 if (kbd_peek() & KEY_MASK)
580                         break;
581 }
582
583 static struct MenuItem sub_items[] =
584 {
585         { (const_iptr_t)"un", 0, (MenuHook)0, NULL },
586         { (const_iptr_t)"du", 0, (MenuHook)0, NULL },
587         { (const_iptr_t)"tr", 0, (MenuHook)0, NULL },
588         { (const_iptr_t)0, 0, NULL, (iptr_t)0 }
589 };
590 static struct Menu sub_menu = { sub_items, "BeRTOS", MF_SAVESEL, &lcd_bitmap, 0, lcd_hx8347_blitBitmap };
591
592 static struct MenuItem main_items[] =
593 {
594         { (const_iptr_t)"Screen saver demo",  0, (MenuHook)screen_saver,  &lcd_bitmap },
595         { (const_iptr_t)"Display brightness", 0, (MenuHook)setBrightness, &lcd_bitmap },
596         { (const_iptr_t)"SD dir",             0, (MenuHook)sd_explorer,   (iptr_t)&lcd_bitmap },
597         { (const_iptr_t)"Reboot",             0, (MenuHook)soft_reset,    &lcd_bitmap },
598         { (const_iptr_t)0,                    0, NULL,                    (iptr_t)0   }
599 };
600 static struct Menu main_menu = { main_items, "BeRTOS", MF_STICKY | MF_SAVESEL, &lcd_bitmap, 0, lcd_hx8347_blitBitmap };
601
602
603 int main(void)
604 {
605         IRQ_ENABLE;
606         kdbg_init();
607
608         LED_INIT();
609         timer_init();
610
611         proc_init();
612         sdram_init();
613         adc_init();
614
615         kprintf("sam3x %s: %d times\n", VERS_HOST, VERS_BUILD);
616
617         dmac_init();
618         i2c_init(&i2c, I2C_BITBANG0, CONFIG_I2C_FREQ);
619         i2s_init(&i2s, SSC0);
620
621         wm8731_init(&wm8731_ctx, &i2c, CODEC_ADDR);
622         wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, 0);
623
624         kprintf("CPU Frequecy:%ld\n", CPU_FREQ);
625
626
627         /* Enable the adc to read internal temperature sensor */
628         hw_enableTempRead();
629
630
631         lcd_hx8347_init();
632         lcd_setBacklight(lcd_brightness);
633
634         gfx_bitmapInit(&lcd_bitmap, raster, LCD_WIDTH, LCD_HEIGHT);
635         gfx_setFont(&lcd_bitmap, &font_luBS14);
636         lcd_hx8347_blitBitmap(&lcd_bitmap);
637
638         kbd_init();
639
640         hp_proc = proc_new(hp_process, NULL, PROC_STACK_SIZE, hp_stack);
641         lp_proc = proc_new(lp_process, NULL, PROC_STACK_SIZE, lp_stack);
642
643         proc_setPri(hp_proc, 2);
644         proc_setPri(lp_proc, 1);
645
646         lcd_hx8347_blitBitmap24(10, 52, BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, bmp_logo);
647         timer_delay(500);
648
649
650         while (1)
651         {
652                 menu_handle(&main_menu);
653                 cpu_relax();
654         }
655
656 }