Don't explore the file list until the wav is in playing.
[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/byteorder.h>
52
53 #include <drv/kbd.h>
54 #include <drv/i2c.h>
55 #include <drv/i2s.h>
56 #include <drv/sd.h>
57 #include <drv/timer.h>
58 #include <drv/lcd_hx8347.h>
59 #include <drv/adc.h>
60 #include <drv/wm8731.h>
61 #include <drv/dmac_sam3.h>
62 #include <drv/sd.h>
63
64 #include <gfx/gfx.h>
65 #include <gfx/font.h>
66 #include <gfx/text.h>
67 #include <gui/menu.h>
68 #include <icons/logo.h>
69
70 #include <io/kfile.h>
71 #include <io/kfile_block.h>
72
73 #include <kern/signal.h>
74 #include <kern/proc.h>
75 #include <kern/msg.h>
76
77 #include <fs/fat.h>
78
79 #include <algo/wav.h>
80
81 #include <struct/list.h>
82
83 #include <verstag.h>
84 #include <buildrev.h>
85
86 #include <stdio.h>
87 #include <string.h>
88
89 // Keyboard
90 #define KEY_MASK (K_LEFT | K_RIGHT)
91
92 /*
93  * Codec has 7-bit address, the eighth is the R/W bit, so we
94  * write the codec address with one bit shifted left
95  */
96 #define CODEC_ADDR 0x36
97
98 // Kernel
99 static PROC_DEFINE_STACK(play_proc_stack, 1024);
100 static PROC_DEFINE_STACK(rec_proc_stack, 1024);
101 MsgPort proc_play_inPort;
102 MsgPort proc_rec_inPort;
103
104 #define MAX_ITEM_NODES    30
105 #define MAX_ITEMS_ROW     15
106 #define NEXT_ITEM_COL     10
107
108 typedef struct FileItemNode
109 {
110         Node n;
111         char file_name[13];
112 } FileItemNode;
113
114 typedef struct AudioMsg
115 {
116         Msg msg;
117         char file_name[13];
118 } AudioMsg;
119
120 FileItemNode item_nodes[MAX_ITEM_NODES];
121
122 uint8_t tmp[10240];
123
124 // SD fat filesystem context
125 FATFS fs;
126 FatFile play_file;
127 FatFile rec_file;
128
129 static Sd sd;
130 static I2c i2c;
131 static I2s i2s;
132 static Wm8731 wm8731_ctx;
133
134 static uint8_t raster[RAST_SIZE(LCD_WIDTH, LCD_HEIGHT)];
135 static Bitmap lcd_bitmap;
136 extern Font font_gohu;
137 static int lcd_brightness = LCD_BACKLIGHT_MAX;
138 static uint16_t headphone_volume = 90;
139 static size_t played_size = 0;
140 static size_t recorded_size = 0;
141 static bool is_playing = false;
142 static bool is_recording = false;
143 static int recorderd_file_idx;
144
145 static void codec_play(struct I2s *i2s, void *_buf, size_t len)
146 {
147         played_size += kfile_read(&play_file.fd, _buf, len);
148         if (played_size >= play_file.fat_file.fsize - sizeof(WavHdr))
149         {
150                 kprintf("stop %d\n", played_size);
151                 i2s_dmaTxStop(i2s);
152                 is_playing = false;
153                 played_size = 0;
154         }
155 }
156
157 static void codec_rec(struct I2s *i2s, void *buf, size_t len)
158 {
159         (void)i2s;
160         ASSERT(buf);
161         ASSERT(len != 0);
162         ASSERT(&rec_file.fd);
163
164         recorded_size += kfile_write(&rec_file.fd, buf, len);
165 }
166
167 static void NORETURN play_proc(void)
168 {
169         while (1)
170         {
171                 event_wait(&proc_play_inPort.event);
172                 AudioMsg *play;
173                 play = (AudioMsg *)msg_get(&proc_play_inPort);
174                 if (play && SD_CARD_PRESENT())
175                 {
176                         timer_delay(10);
177
178                         bool sd_ok = sd_init(&sd, NULL, 0);
179                         FRESULT result;
180
181                         if (sd_ok)
182                         {
183                                 kprintf("Mount FAT filesystem.\n");
184
185                                 result = f_mount(0, &fs);
186                                 if (result != FR_OK)
187                                 {
188                                         kprintf("Mounting FAT volumes error[%d]\n", result);
189                                         sd_ok = false;
190                                 }
191
192                                 if (sd_ok)
193                                 {
194                                         result = fatfile_open(&play_file, play->file_name,  FA_OPEN_EXISTING | FA_READ);
195                                         if (result == FR_OK)
196                                         {
197                                                 kprintf("Open file: %s size %ld\n", play->file_name, play_file.fat_file.fsize);
198                                                 WavHdr wav;
199                                                 kfile_read(&play_file.fd, &wav, sizeof(WavHdr));
200                                                 if (wav_checkHdr(&wav, 1, CONFIG_CHANNEL_NUM, CONFIG_SAMPLE_FREQ, CONFIG_WORD_BIT_SIZE) != -1)
201                                                 {
202                                                         kputs("Wav file play..\n");
203
204                                                         wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, headphone_volume);
205                                                         is_playing = true;
206                                                         i2s_dmaStartTxStreaming(&i2s, tmp, sizeof(tmp), sizeof(tmp) / 2, codec_play);
207
208                                                         wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, 0);
209                                                 }
210
211                                                 // Flush data and close the files.
212                                                 kfile_flush(&play_file.fd);
213                                                 kfile_close(&play_file.fd);
214                                         }
215                                         else
216                                         {
217                                                 kprintf("Unable to open file: '%s' error[%d]\n", play->file_name, result);
218                                         }
219                                 }
220                                 f_mount(0, NULL);
221                         }
222                 }
223         }
224 }
225
226
227 static void NORETURN rec_proc(void)
228 {
229         while (1)
230         {
231                 event_wait(&proc_rec_inPort.event);
232                 AudioMsg *rec;
233                 rec = (AudioMsg *)msg_get(&proc_rec_inPort);
234                 if (rec && SD_CARD_PRESENT())
235                 {
236                         timer_delay(10);
237
238                         bool sd_ok = sd_init(&sd, NULL, 0);
239                         FRESULT result;
240
241                         if (sd_ok)
242                         {
243                                 kprintf("Mount FAT filesystem.\n");
244
245                                 result = f_mount(0, &fs);
246                                 if (result != FR_OK)
247                                 {
248                                         kprintf("Mounting FAT volumes error[%d]\n", result);
249                                         sd_ok = false;
250                                 }
251
252                                 if (sd_ok)
253                                 {
254                                         result = fatfile_open(&rec_file, rec->file_name,  FA_CREATE_ALWAYS | FA_WRITE);
255                                         if (result == FR_OK)
256                                         {
257                                                 kprintf("Open file: %s size %ld\n", rec->file_name, rec_file.fat_file.fsize);
258                                                 kputs("Rec Wav file..\n");
259                                                 is_recording = true;
260                                                 // Leave place for wav header
261                                                 kfile_seek(&rec_file.fd, sizeof(WavHdr), KSM_SEEK_SET);
262
263                                                 i2s_dmaStartRxStreaming(&i2s, tmp, sizeof(tmp), sizeof(tmp) / 2, codec_rec);
264
265                                                 // write header
266                                                 WavHdr wav;
267                                                 wav_writeHdr(&wav, recorded_size, 1, CONFIG_CHANNEL_NUM, CONFIG_SAMPLE_FREQ, CONFIG_WORD_BIT_SIZE);
268                                                 kfile_seek(&rec_file.fd, 0, KSM_SEEK_SET);
269                                                 kfile_write(&rec_file.fd, &wav, sizeof(WavHdr));
270
271                                                 // Flush data and close the files.
272                                                 kfile_flush(&rec_file.fd);
273                                                 kfile_close(&rec_file.fd);
274                                         }
275                                         else
276                                         {
277                                                 kprintf("Unable to open file: '%s' error[%d]\n", rec->file_name, result);
278                                         }
279                                 }
280                                 f_mount(0, NULL);
281                         }
282                 }
283         }
284 }
285
286
287 INLINE void start_play(char *file_name)
288 {
289         played_size = 0;
290
291         AudioMsg play_msg;
292         memcpy(play_msg.file_name, file_name, sizeof(play_msg.file_name));
293         msg_put(&proc_play_inPort, &play_msg.msg);
294 }
295
296 INLINE void stop_play(void)
297 {
298         i2s_dmaTxStop(&i2s);
299         is_playing = false;
300 }
301
302
303 INLINE void start_rec(char *file_name)
304 {
305         recorded_size = 0;
306
307         AudioMsg rec_msg;
308         memcpy(rec_msg.file_name, file_name, sizeof(rec_msg.file_name));
309         msg_put(&proc_rec_inPort, &rec_msg.msg);
310 }
311
312 INLINE void stop_rec(void)
313 {
314         i2s_dmaRxStop(&i2s);
315         is_recording = false;
316 }
317
318 INLINE FileItemNode *select_item(Bitmap *bm, List *file_list, int select_idx)
319 {
320         FileItemNode *item;
321         FileItemNode *select_node;
322         int col = 0;
323         int row = 0;
324
325         gfx_bitmapClear(bm);
326         select_node = (FileItemNode *)LIST_HEAD(file_list);
327     FOREACH_NODE(item, file_list)
328         {
329                 if (row > MAX_ITEMS_ROW)
330                 {
331                         row = 1;
332                         col = NEXT_ITEM_COL;
333                 }
334
335                 text_style(bm, 0, STYLEF_MASK);
336                 if (select_idx <= MAX_ITEMS_ROW)
337                 {
338                         if (row == select_idx && col == 0)
339                         {
340                                 text_style(bm, STYLEF_INVERT, STYLEF_INVERT);
341                                 select_node = item;
342                         }
343                 }
344                 else
345                 {
346                         if (row == (select_idx - MAX_ITEMS_ROW) && col == NEXT_ITEM_COL)
347                         {
348                                 text_style(bm, STYLEF_INVERT, STYLEF_INVERT);
349                                 select_node = item;
350                         }
351                 }
352
353                 text_xprintf(bm, row, col, TEXT_NORMAL, "%s", item->file_name);
354                 row++;
355         }
356
357         lcd_hx8347_blitBitmap(bm);
358
359         return select_node;
360 }
361
362 static void play_menu(Bitmap *bm)
363 {
364         List file_list;
365         LIST_INIT(&file_list);
366         int file_list_size = 0;
367         gfx_bitmapClear(bm);
368
369         memcpy (&item_nodes[0].file_name, "<- Return..", sizeof(item_nodes[0].file_name));
370         ADDTAIL(&file_list, &item_nodes[0].n);
371
372         if (SD_CARD_PRESENT())
373         {
374                 timer_delay(10);
375
376                 bool sd_ok = sd_init(&sd, NULL, 0);
377                 FRESULT result;
378
379                 if (sd_ok)
380                 {
381                         kprintf("Mount FAT filesystem.\n");
382                         result = f_mount(0, &fs);
383                         if (result != FR_OK)
384                         {
385                                 kprintf("Mounting FAT volumes error[%d]\n", result);
386                                 sd_ok = false;
387                         }
388
389                         if (sd_ok)
390                         {
391                                 FILINFO fno;
392                                 DIR dir;
393
394                                 kputs("open dir\n");
395                                 /* Open the directory */
396                                 result = f_opendir(&dir, "/");
397                                 if (result == FR_OK)
398                                 {
399                                         // First element is reserved for "return" label
400                                         for (int i = 1;; i++)
401                                         {
402                                                 /* Read a directory item */
403                                                 result = f_readdir(&dir, &fno);
404                                                 if (result != FR_OK || fno.fname[0] == 0)
405                                                         break;  /* Break on error or end of dir */
406                                                 if (fno.fname[0] == '.')
407                                                         continue; /* Ignore dot entry */
408                                                 if (fno.fattrib & AM_DIR)
409                                                         continue;
410                                                 else
411                                                 {
412                                                         if (i < MAX_ITEM_NODES)
413                                                         {
414                                                                 memcpy (&item_nodes[i].file_name, fno.fname, sizeof(item_nodes[i].file_name));
415                                                                 ADDTAIL(&file_list, &item_nodes[i].n);
416                                                                 file_list_size++;
417                                                                 kprintf("%s\n", item_nodes[i].file_name);
418                                                         }
419                                                         else
420                                                         {
421                                                                 kputs("No enought space to store items in list\n");
422                                                                 break;
423                                                         }
424                                                 }
425                                         }
426                                 }
427                         }
428                         f_mount(0, NULL);
429                         kprintf("Umount\n");
430                 }
431         }
432         else
433         {
434                 kputs("No card insert..\n");
435                 text_xprintf(bm, 5, 0, TEXT_CENTER | TEXT_FILL, "%s", "No card insert..");
436         }
437
438         int idx = 0;
439         FileItemNode *selected_node = NULL;
440         select_item(bm, &file_list, idx);
441         while (1)
442         {
443                 keymask_t key = kbd_peek();
444                 if ((key & K_LEFT) && !is_playing)
445                 {
446                         idx++;
447                         if (idx > file_list_size)
448                                 idx = 0;
449
450                         selected_node = select_item(bm, &file_list, idx);
451                 }
452                 if (key & K_RIGHT)
453                 {
454                         if (idx == 0)
455                                 break;
456
457                         if (!is_playing)
458                                 start_play(selected_node->file_name);
459                         else
460                                 stop_play();
461                 }
462
463                 cpu_relax();
464         }
465 }
466
467
468 static void rec_menu(Bitmap *bm)
469 {
470         gfx_bitmapClear(bm);
471         text_style(bm, STYLEF_BOLD | STYLEF_UNDERLINE, STYLEF_BOLD | STYLEF_UNDERLINE);
472         text_xprintf(bm, 0, 0, TEXT_CENTER | TEXT_FILL, "Microphone recorder.");
473         text_style(bm, 0, STYLEF_MASK);
474         text_xprintf(bm, 2, 0, TEXT_NORMAL, "Press RIGHT button to start recording");
475         text_xprintf(bm, 3, 0, TEXT_NORMAL, "and to stop it re-press RIGHT button.");
476         lcd_hx8347_blitBitmap(bm);
477
478         ticks_t start= 0;
479         while (1)
480         {
481                 keymask_t key = kbd_peek();
482                 if (key & K_LEFT)
483                 {
484                         break;
485                 }
486                 if (key & K_RIGHT)
487                 {
488                         char file_name[13];
489                         memset(file_name, 0, sizeof(file_name));
490                         sprintf(file_name, "REC%d.WAV", recorderd_file_idx);
491                         kprintf("rec %s\n", file_name);
492
493                         if (!is_recording)
494                         {
495                                 start_rec(file_name);
496                                 text_xprintf(bm, 5, 0, TEXT_CENTER | TEXT_FILL, "Start recording on file: %s", file_name);
497                                 text_xprintf(bm, 6, 0, TEXT_CENTER | TEXT_FILL, " ");
498                                 lcd_hx8347_blitBitmap(bm);
499                                 start = timer_clock();
500                         }
501                         else
502                         {
503                                 stop_rec();
504                                 recorderd_file_idx++;
505                                 text_xprintf(bm, 5, 0, TEXT_CENTER | TEXT_FILL, "Stop recording: %s", file_name);
506                                 mtime_t elaps = ticks_to_ms(timer_clock() - start);
507                                 text_xprintf(bm, 6, 0, TEXT_CENTER | TEXT_FILL, "Recorded: %ld.%ldsec", elaps / 1000, elaps % 1000);
508                                 lcd_hx8347_blitBitmap(bm);
509                         }
510                 }
511
512                 cpu_relax();
513         }
514 }
515
516
517
518 /*
519  * Lcd
520  */
521 static void setBrightness(Bitmap *bm)
522 {
523         while (1)
524         {
525                 gfx_bitmapClear(bm);
526                 text_xprintf(bm, 1, 0, TEXT_FILL | TEXT_CENTER, "Brightness: %d", lcd_brightness);
527                 text_xprintf(bm, 3, 0, TEXT_FILL | TEXT_CENTER, "RIGHT key: change");
528                 text_xprintf(bm, 4, 0, TEXT_FILL | TEXT_CENTER, "LEFT  key: back  ");
529                 lcd_hx8347_blitBitmap(bm);
530
531                 keymask_t mask = kbd_get();
532
533                 if (mask & K_LEFT)
534                         break;
535                 else if (mask & K_RIGHT)
536                 {
537                         if (++lcd_brightness > LCD_BACKLIGHT_MAX)
538                                 lcd_brightness = 0;
539                         lcd_setBacklight(lcd_brightness);
540                 }
541         }
542 }
543
544 static void setVolume(Bitmap *bm)
545 {
546         gfx_bitmapClear(bm);
547         text_style(bm, STYLEF_BOLD | STYLEF_UNDERLINE, STYLEF_BOLD | STYLEF_UNDERLINE);
548         text_xprintf(bm, 0, 0, TEXT_CENTER, "Headphone Volume");
549         text_style(bm, 0, STYLEF_MASK);
550         text_xprintf(bm, 2, 0, TEXT_NORMAL, "Turn VR1 potentiometer to adjust it.");
551
552         while (1)
553         {
554                 headphone_volume = ADC_RANGECONV(adc_read(1), 0, 100);
555                 text_xprintf(bm, 5, 0, TEXT_FILL | TEXT_CENTER, "Volume %d%%", headphone_volume);
556                 lcd_hx8347_blitBitmap(bm);
557
558                 timer_delay(400);
559                 if (kbd_peek() & KEY_MASK)
560                         break;
561         }
562 }
563
564 static void NORETURN soft_reset(Bitmap * bm)
565 {
566         int i;
567
568         gfx_bitmapClear(bm);
569         for (i = 5; i; --i)
570         {
571                 text_xprintf(bm, 2, 0, TEXT_FILL | TEXT_CENTER, "%d", i);
572                 lcd_hx8347_blitBitmap(bm);
573                 timer_delay(1000);
574         }
575         text_xprintf(bm, 2, 0, TEXT_FILL | TEXT_CENTER, "REBOOT");
576         lcd_hx8347_blitBitmap(bm);
577         timer_delay(1000);
578
579         /* Perform a software reset request */
580         HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ;
581         UNREACHABLE();
582 }
583
584
585 static struct MenuItem main_items[] =
586 {
587         { (const_iptr_t)"Play SD file",       0, (MenuHook)play_menu,     (iptr_t)&lcd_bitmap },
588         { (const_iptr_t)"Record file on SD",  0, (MenuHook)rec_menu,      (iptr_t)&lcd_bitmap },
589         { (const_iptr_t)"Set brightness",     0, (MenuHook)setBrightness, (iptr_t)&lcd_bitmap },
590         { (const_iptr_t)"Set volume",         0, (MenuHook)setVolume,     (iptr_t)&lcd_bitmap },
591         { (const_iptr_t)"Reboot",             0, (MenuHook)soft_reset,    (iptr_t)&lcd_bitmap },
592         { (const_iptr_t)0,                    0, NULL,                    (iptr_t)0   }
593 };
594 static struct Menu main_menu = { main_items, "BeRTOS", MF_STICKY | MF_SAVESEL, &lcd_bitmap, 0, lcd_hx8347_blitBitmap };
595
596
597 int main(void)
598 {
599         IRQ_ENABLE;
600         kdbg_init();
601
602         LED_INIT();
603         timer_init();
604
605         proc_init();
606         sdram_init();
607         adc_init();
608
609         kprintf("sam3x %s: %d times\n", VERS_HOST, VERS_BUILD);
610
611         dmac_init();
612         i2c_init(&i2c, I2C_BITBANG0, CONFIG_I2C_FREQ);
613         i2s_init(&i2s, SSC0);
614
615         wm8731_init(&wm8731_ctx, &i2c, CODEC_ADDR);
616         wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, 0);
617
618         kprintf("CPU Frequecy:%ld\n", CPU_FREQ);
619
620         lcd_hx8347_init();
621         lcd_setBacklight(lcd_brightness);
622
623         gfx_bitmapInit(&lcd_bitmap, raster, LCD_WIDTH, LCD_HEIGHT);
624         gfx_setFont(&lcd_bitmap, &font_luBS14);
625         lcd_hx8347_blitBitmap(&lcd_bitmap);
626
627         kbd_init();
628
629         proc_new(play_proc, NULL, sizeof(play_proc_stack), play_proc_stack);
630         proc_new(rec_proc, NULL, sizeof(rec_proc_stack), rec_proc_stack);
631         msg_initPort(&proc_play_inPort, event_createGeneric());
632         msg_initPort(&proc_rec_inPort, event_createGeneric());
633
634         lcd_hx8347_blitBitmap24(10, 52, BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, bmp_logo);
635         timer_delay(500);
636
637
638         while (1)
639         {
640                 menu_handle(&main_menu);
641                 cpu_relax();
642         }
643
644 }