/* RDA5807 based FM receiver
Copyright (C) 2022 Doctor Volt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include //Seeed Arduino SFUD 2.0.1
//#include %s\r\n", file, line, dsc);
delay(100);
}
#endif
/* Interrupt driven periodic handler */
static void lv_tick_handler(void)
{
lv_tick_inc(LVGL_TICK_PERIOD);
}
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint16_t c;
tft.startWrite(); /* Start new TFT transaction */
tft.setAddrWindow(area->x1, area->y1, (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1)); /* set the working window */
for (int y = area->y1; y <= area->y2; y++)
{
for (int x = area->x1; x <= area->x2; x++)
{
c = color_p->full;
tft.writeColor(c, 1);
color_p++;
}
}
tft.endWrite(); /* terminate TFT transaction */
lv_disp_flush_ready(disp); /* tell lvgl that flushing is done */
}
/* Callback for processing the keys */
bool keys_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
//delay(100); //Debounce
uint8_t last_key = 0;
if (!digitalRead(WIO_5S_UP))
last_key = LV_KEY_UP;
else if (!digitalRead(WIO_5S_DOWN))
last_key = LV_KEY_DOWN;
else if (!digitalRead(WIO_5S_LEFT))
last_key = LV_KEY_PREV;
else if (!digitalRead(WIO_5S_RIGHT))
last_key = LV_KEY_NEXT;
else if (!digitalRead(WIO_5S_PRESS))
last_key = LV_KEY_ENTER;
if (last_key)
data->state = LV_INDEV_STATE_PR;
else
data->state = LV_INDEV_STATE_REL;
data->key = last_key; /*Get the last pressed or released key*/
return false; /*No buffering now so no more data read*/
}
// Task that updates GUI (rssi rds, etc.)
void update_gui_task(lv_task_t *task)
{
gui_show_status(rda.getRssi(), rda.isStereo());
}
void update_rds_task(lv_task_t *task)
{
if(rda.hasRdsInfo())
{
gui_show_rdsinfo("%s\n%s", rda.getRdsText0A(), rda.getRdsText());
}
}
void setup()
{
Serial.begin(115200);
lv_init();
#if USE_LV_LOG != 0
lv_log_register_print(my_print); /* register print function for debugging */
#endif
tft.init(); // TFT init
tft.setRotation(3); // Landscape orientation
lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);
/*Initialize the display*/
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = SCR_RES_X;
disp_drv.ver_res = SCR_RES_Y;
disp_drv.flush_cb = my_disp_flush;
disp_drv.buffer = &disp_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the keys*/
pinMode(NAV_NEXT, INPUT_PULLUP); //Next item
pinMode(NAV_PREV, INPUT_PULLUP); //Previous item
pinMode(SET_UP, INPUT_PULLUP); //Value up
pinMode(SET_DOWN, INPUT_PULLUP); //Value down
pinMode(ENTER, INPUT_PULLUP); //Enter
lv_indev_drv_t key_drv;
lv_indev_drv_init(&key_drv);
key_drv.type = LV_INDEV_TYPE_KEYPAD;
key_drv.read_cb = keys_read; // Callback that handles joystick and keys
lv_indev_drv_register(&key_drv);
wio_key_dev = lv_indev_drv_register(&key_drv);
sfud_init();
#ifdef SFUD_USING_QSPI
sfud_qspi_fast_read_enable(sfud_get_device(SFUD_W25Q32_DEVICE_INDEX), 2);
#endif
flash = sfud_get_device_table() + 0;
//sfud_erase(flash, 0, sizeof(ConfigData));
sfud_err flash_status = sfud_read(flash, 0, 1, (uint8_t *)p_Config); //Read one byte to test if flash is availabe
if(flash_status == SFUD_SUCCESS && p_Config->test == 0xab) //and there is valid data
{
sfud_read(flash, 0, sizeof(ConfigData), (uint8_t *)p_Config);
}
p_Config->test = 0xab;
lv_obj_t *screen = lv_obj_create(NULL, NULL);
gui_init(screen);
lv_scr_load(screen);
if(flash_status != SFUD_SUCCESS)
{
lv_obj_t *error = lv_label_create(screen, NULL);
lv_obj_set_style_local_text_color(error, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_label_set_text_fmt(error, "Flash memory not available.\nCannot save any settings. Code: %d", flash_status);
lv_obj_align(error, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 10, -10);
}
lv_task_t *update_gui = lv_task_create(update_gui_task, 2000, LV_TASK_PRIO_MID, NULL);
lv_task_ready(update_gui);
lv_task_t *update_rds = lv_task_create(update_rds_task, 500, LV_TASK_PRIO_MID, NULL);
rda.setup();
delay(1000);
rda_set_config();
rda.setFrequency(p_Config->frequency);
rda.setVolume(p_Config->volume);
}
void loop()
{
lv_tick_handler();
lv_task_handler(); /* let the GUI do its work */
delay(5);
}
void save_settings()
{
sfud_erase_write(flash, 0, sizeof(ConfigData), (uint8_t *)p_Config);
}
// Radio control functions
void rda_get_frequency()
{
gui_show_frequency(rda.getFrequency());
}
void rda_seek(bool up)
{
rda.seek(0, up, rda_get_frequency);
p_Config->frequency = rda.getRealFrequency();
sfud_erase_write(flash, 0, sizeof(ConfigData), (uint8_t *)p_Config);
}
void rda_set_next_frequency(uint16_t freq)
{
rda.setFrequency(freq);
p_Config->frequency = rda.getRealFrequency();
gui_show_frequency(p_Config->frequency);
sfud_erase_write(flash, 0, sizeof(ConfigData), (uint8_t *)p_Config);
}
void rda_set_volume()
{
rda.setVolume(p_Config->volume);
sfud_erase_write(flash, 0, sizeof(ConfigData), (uint8_t *)p_Config);
}
void rda_set_config()
{
rda.setMono(p_Config->mono);
rda.setBass(p_Config->bass_boost);
rda.setBand(p_Config->band);
rda.setFmDeemphasis(!p_Config->usa);
p_Config->usa ? rda.setRBDS(p_Config->rds_on) : rda.setRDS(p_Config->rds_on);
rda.setSpace(p_Config->space);
if(p_Config->frequency == 0)
p_Config->frequency = rda.getRealFrequency();
rda.setRdsFifo(true);
//char dbg[64];
//sprintf(dbg, "Mono: %d, Bass: %d, band: %d, space: %d, USA: %d", p_Config->mono, p_Config->bass_boost, p_Config->band, p_Config->space, p_Config->usa);
//Serial.println(dbg);
}