4 * This file is part of BeRTOS.
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.
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.
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
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.
29 * Copyright 2011 Develer S.r.l. (http://www.develer.com/)
33 * \brief Atmel SAM3X-EK wav player example.
35 * \author Stefano Fedrigo <aleph@develer.com>
36 * \author Daniele Basile <asterix@develer.com>
41 #include "hw/hw_led.h"
42 #include "hw/hw_lcd.h"
43 #include "hw/hw_adc.h"
44 #include "hw/hw_sdram.h"
47 #include "cfg/cfg_i2s.h"
49 #include <cfg/debug.h>
52 #include <cpu/byteorder.h>
58 #include <drv/timer.h>
59 #include <drv/lcd_hx8347.h>
61 #include <drv/wm8731.h>
62 #include <drv/dmac_sam3.h>
69 #include <icons/logo.h>
72 #include <io/kfile_block.h>
74 #include <kern/signal.h>
75 #include <kern/proc.h>
82 #include <struct/list.h>
91 #define KEY_MASK (K_LEFT | K_RIGHT)
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
97 #define CODEC_ADDR 0x36
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;
105 #define MAX_ITEM_NODES 30
106 #define MAX_ITEMS_ROW 15
107 #define NEXT_ITEM_COL 10
109 typedef struct FileItemNode
115 typedef struct AudioMsg
121 FileItemNode item_nodes[MAX_ITEM_NODES];
125 // SD fat filesystem context
133 static Wm8731 wm8731_ctx;
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;
146 static void codec_play(struct I2s *i2s, void *_buf, size_t len)
148 played_size += kfile_read(&play_file.fd, _buf, len);
149 if (played_size >= play_file.fat_file.fsize - sizeof(WavHdr))
151 kprintf("stop %d\n", played_size);
158 static void codec_rec(struct I2s *i2s, void *buf, size_t len)
163 ASSERT(&rec_file.fd);
165 recorded_size += kfile_write(&rec_file.fd, buf, len);
168 static void NORETURN play_proc(void)
172 event_wait(&proc_play_inPort.event);
174 play = (AudioMsg *)msg_get(&proc_play_inPort);
175 if (play && SD_CARD_PRESENT())
179 bool sd_ok = sd_init(&sd, NULL, 0);
184 kprintf("Mount FAT filesystem.\n");
186 result = f_mount(0, &fs);
189 kprintf("Mounting FAT volumes error[%d]\n", result);
195 result = fatfile_open(&play_file, play->file_name, FA_OPEN_EXISTING | FA_READ);
198 kprintf("Open file: %s size %ld\n", play->file_name, play_file.fat_file.fsize);
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)
203 kputs("Wav file play..\n");
205 wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, headphone_volume);
207 i2s_dmaStartTxStreaming(&i2s, tmp, sizeof(tmp), sizeof(tmp) / 2, codec_play);
209 wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, 0);
212 // Flush data and close the files.
213 kfile_flush(&play_file.fd);
214 kfile_close(&play_file.fd);
218 kprintf("Unable to open file: '%s' error[%d]\n", play->file_name, result);
228 static void NORETURN rec_proc(void)
232 event_wait(&proc_rec_inPort.event);
234 rec = (AudioMsg *)msg_get(&proc_rec_inPort);
235 if (rec && SD_CARD_PRESENT())
239 bool sd_ok = sd_init(&sd, NULL, 0);
244 kprintf("Mount FAT filesystem.\n");
246 result = f_mount(0, &fs);
249 kprintf("Mounting FAT volumes error[%d]\n", result);
255 result = fatfile_open(&rec_file, rec->file_name, FA_CREATE_ALWAYS | FA_WRITE);
258 kprintf("Open file: %s size %ld\n", rec->file_name, rec_file.fat_file.fsize);
259 kputs("Rec Wav file..\n");
261 // Leave place for wav header
262 kfile_seek(&rec_file.fd, sizeof(WavHdr), KSM_SEEK_SET);
264 i2s_dmaStartRxStreaming(&i2s, tmp, sizeof(tmp), sizeof(tmp) / 2, codec_rec);
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));
272 // Flush data and close the files.
273 kfile_flush(&rec_file.fd);
274 kfile_close(&rec_file.fd);
278 kprintf("Unable to open file: '%s' error[%d]\n", rec->file_name, result);
288 INLINE void start_play(char *file_name)
293 memcpy(play_msg.file_name, file_name, sizeof(play_msg.file_name));
294 msg_put(&proc_play_inPort, &play_msg.msg);
297 INLINE void stop_play(void)
304 INLINE void start_rec(char *file_name)
309 memcpy(rec_msg.file_name, file_name, sizeof(rec_msg.file_name));
310 msg_put(&proc_rec_inPort, &rec_msg.msg);
313 INLINE void stop_rec(void)
316 is_recording = false;
319 INLINE FileItemNode *select_item(Bitmap *bm, List *file_list, int select_idx)
322 FileItemNode *select_node;
327 select_node = (FileItemNode *)LIST_HEAD(file_list);
328 FOREACH_NODE(item, file_list)
330 if (row > MAX_ITEMS_ROW)
336 text_style(bm, 0, STYLEF_MASK);
337 if (select_idx <= MAX_ITEMS_ROW)
339 if (row == select_idx && col == 0)
341 text_style(bm, STYLEF_INVERT, STYLEF_INVERT);
347 if (row == (select_idx - MAX_ITEMS_ROW) && col == NEXT_ITEM_COL)
349 text_style(bm, STYLEF_INVERT, STYLEF_INVERT);
354 text_xprintf(bm, row, col, TEXT_NORMAL, "%s", item->file_name);
358 lcd_hx8347_blitBitmap(bm);
363 static void play_menu(Bitmap *bm)
366 LIST_INIT(&file_list);
367 int file_list_size = 0;
370 memcpy (&item_nodes[0].file_name, "<- Return..", sizeof(item_nodes[0].file_name));
371 ADDTAIL(&file_list, &item_nodes[0].n);
373 if (SD_CARD_PRESENT())
377 bool sd_ok = sd_init(&sd, NULL, 0);
382 kprintf("Mount FAT filesystem.\n");
383 result = f_mount(0, &fs);
386 kprintf("Mounting FAT volumes error[%d]\n", result);
396 /* Open the directory */
397 result = f_opendir(&dir, "/");
400 // First element is reserved for "return" label
401 for (int i = 1;; i++)
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)
413 if (i < MAX_ITEM_NODES)
415 memcpy (&item_nodes[i].file_name, fno.fname, sizeof(item_nodes[i].file_name));
416 ADDTAIL(&file_list, &item_nodes[i].n);
418 kprintf("%s\n", item_nodes[i].file_name);
422 kputs("No enought space to store items in list\n");
435 kputs("No card insert..\n");
436 text_xprintf(bm, 5, 0, TEXT_CENTER | TEXT_FILL, "%s", "No card insert..");
440 FileItemNode *selected_node = NULL;
441 select_item(bm, &file_list, idx);
444 keymask_t key = kbd_peek();
445 if ((key & K_LEFT) && !is_playing)
448 if (idx > file_list_size)
451 selected_node = select_item(bm, &file_list, idx);
459 start_play(selected_node->file_name);
469 static void rec_menu(Bitmap *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);
482 keymask_t key = kbd_peek();
483 if ((key & K_LEFT) && (!is_recording))
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);
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();
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);
522 static void setBrightness(Bitmap *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);
532 keymask_t mask = kbd_get();
536 else if (mask & K_RIGHT)
538 if (++lcd_brightness > LCD_BACKLIGHT_MAX)
540 lcd_setBacklight(lcd_brightness);
545 static void setVolume(Bitmap *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.");
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);
560 if (kbd_peek() & KEY_MASK)
565 static void NORETURN soft_reset(Bitmap * bm)
572 text_xprintf(bm, 2, 0, TEXT_FILL | TEXT_CENTER, "%d", i);
573 lcd_hx8347_blitBitmap(bm);
576 text_xprintf(bm, 2, 0, TEXT_FILL | TEXT_CENTER, "REBOOT");
577 lcd_hx8347_blitBitmap(bm);
580 /* Perform a software reset request */
581 HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ;
586 static struct MenuItem main_items[] =
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 }
595 static struct Menu main_menu = { main_items, "BeRTOS", MF_STICKY | MF_SAVESEL, &lcd_bitmap, 0, lcd_hx8347_blitBitmap };
610 kprintf("sam3x %s: %d times\n", VERS_HOST, VERS_BUILD);
613 i2c_init(&i2c, I2C_BITBANG0, CONFIG_I2C_FREQ);
614 i2s_init(&i2s, SSC0);
616 wm8731_init(&wm8731_ctx, &i2c, CODEC_ADDR);
617 wm8731_setVolume(&wm8731_ctx, WM8731_HEADPHONE, 0);
619 kprintf("CPU Frequecy:%ld\n", CPU_FREQ);
622 lcd_setBacklight(lcd_brightness);
624 gfx_bitmapInit(&lcd_bitmap, raster, LCD_WIDTH, LCD_HEIGHT);
625 gfx_setFont(&lcd_bitmap, &font_luBS14);
626 lcd_hx8347_blitBitmap(&lcd_bitmap);
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());
635 lcd_hx8347_blitBitmap24(10, 52, BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, bmp_logo);
641 menu_handle(&main_menu);