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