From a1176112ce5423bdc57de4403b3e194a473ef7a8 Mon Sep 17 00:00:00 2001 From: JiXieShi Date: Wed, 27 Nov 2024 13:49:34 +0800 Subject: [PATCH] ADD FLASH(flash.h flash.cpp flash_def.h flash_cfg.h flash_sfdp.cpp) ADD RunTimer(log.h ticks.h ticks.cpp) ADD sim_key form sdl --- CMakeLists.txt | 4 +- SDL3 | 2 +- demo/lvgl/test.c | 6 +- demo/oled/test.c | 18 +- demo/task/test.c | 27 +- demo/tft/test.c | 15 +- lib/CMakeLists.txt | 1 + lib/flash/flash.cpp | 1038 +++++++++++++++++++++++++++++++++++ lib/flash/flash_sfdp.cpp | 359 ++++++++++++ lib/flash/inc/flash.h | 208 +++++++ lib/flash/inc/flash_cfg.h | 27 + lib/flash/inc/flash_def.h | 406 ++++++++++++++ lib/oled/inc/oled.h | 6 +- lib/oled/oled.cpp | 4 +- lib/tft/inc/ST7735.h | 14 +- lib/tft/inc/tft.h | 37 ++ lib/tft/tft.cpp | 182 +++++- lib/utils/inc/log.h | 192 ++++++- lib/utils/inc/ticks.h | 806 +++++++++++++++++++++++++++ lib/utils/inc/tool.h | 39 +- lib/utils/ticks.cpp | 631 +++++++++++++++++++++ main.c | 8 +- sim/display/sim_display.cpp | 13 +- sim/key/key.cpp | 30 +- sim/key/sim_key.h | 3 + sim/lvgl/lv_conf.h | 2 +- sim/lvgl/lv_port_disp.cpp | 20 +- sim/sdl/sim_sdl.cpp | 38 -- sim/sdl/sim_sdl.h | 36 -- 29 files changed, 3993 insertions(+), 179 deletions(-) create mode 100644 lib/flash/flash.cpp create mode 100644 lib/flash/flash_sfdp.cpp create mode 100644 lib/flash/inc/flash.h create mode 100644 lib/flash/inc/flash_cfg.h create mode 100644 lib/flash/inc/flash_def.h create mode 100644 lib/utils/inc/ticks.h create mode 100644 lib/utils/ticks.cpp delete mode 100644 sim/sdl/sim_sdl.cpp delete mode 100644 sim/sdl/sim_sdl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ea0b9f8..9d66f45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ link_directories(easyx/lib64) file(GLOB_RECURSE SOURCES "demo/*/*.*" "sim/*/*.*") -message(${SOURCES}) +#message(${SOURCES}) add_subdirectory(SDL3 EXCLUDE_FROM_ALL) link_libraries(SDL3::SDL3) @@ -36,7 +36,7 @@ add_executable(HW_Lib main.c ${SOURCES}) #导入库 add_subdirectory(lib) target_link_libraries(HW_Lib HW_LIB_List HW_LIB_Task HW_LIB_Printf HW_LIB_Utils HW_LIB_Iic - HW_LIB_Spi HW_LIB_Key HW_LIB_Oled HW_LIB_Font HW_LIB_Tft lvgl::lvgl + HW_LIB_Spi HW_LIB_Key HW_LIB_Oled HW_LIB_Font HW_LIB_Tft lvgl::lvgl lvgl::examples lvgl::demos ) add_custom_command(TARGET HW_Lib POST_BUILD diff --git a/SDL3 b/SDL3 index 89c6bc5..1266210 160000 --- a/SDL3 +++ b/SDL3 @@ -1 +1 @@ -Subproject commit 89c6bc5f5022e71433a9e4eb1a2edc6d79be71f2 +Subproject commit 1266210685a14b344a9049938677a89a092dfa5c diff --git a/demo/lvgl/test.c b/demo/lvgl/test.c index 564f983..6bf77fb 100644 --- a/demo/lvgl/test.c +++ b/demo/lvgl/test.c @@ -2,7 +2,9 @@ #include "lv_port_disp.h" #include "lv_port_indev.h" #include "lvgl.h" +#include "demos/lv_demos.h" #include +//#include #include "t_lvgl.h" #include "SDL3/SDL.h" @@ -38,8 +40,8 @@ int Test_lvgl(void *pVoid) { lv_port_disp_init(); // lv_port_indev_init(); - lv_example_get_started_1(); -// lv_demo_widgets(); +// lv_example_get_started_1(); + lv_demo_widgets(); // printf("\nTEST Widgets\n"); while (1) { diff --git a/demo/oled/test.c b/demo/oled/test.c index 3ca105d..7b79e97 100644 --- a/demo/oled/test.c +++ b/demo/oled/test.c @@ -131,16 +131,16 @@ int Test_OLED(void *pVoid) { // OLED_ShowPic(&oled, 0, 0, 64, 64, BMP1); OLED_Refresh(&oled); _beginthread(Get_Key, 0, NULL); - pageinit(); +// pageinit(); while (1) { - if (pageid > 4)pageid = 0; - item_h = pagesearch(pageid).item_h; - item_w = pagesearch(pageid).item_w; -// pagesearch(pageid).page(&oled); -// sprintf(buf, "DATA:%d", s); -// OLED_ShowString(&oled, 2, 51, buf, 12); - OLED_Refresh(&oled); - Sleep(200); +// if (pageid > 4)pageid = 0; +// item_h = pagesearch(pageid).item_h; +// item_w = pagesearch(pageid).item_w; +//// pagesearch(pageid).page(&oled); +//// sprintf(buf, "DATA:%d", s); +//// OLED_ShowString(&oled, 2, 51, buf, 12); +// OLED_Refresh(&oled); +// Sleep(200); } SIM_OLED_STOP(); } diff --git a/demo/task/test.c b/demo/task/test.c index 7404e28..08fac3b 100644 --- a/demo/task/test.c +++ b/demo/task/test.c @@ -1,6 +1,7 @@ #include #include "stdio.h" #include "task.h" +#include "log.h" Task_t *task1; Task_t *task2; @@ -34,22 +35,28 @@ typedef struct CustomUserData { void exampleTimer4Callback(Task_t *task, void *userData) { CustomUserData *customUserData = (CustomUserData *) userData; customUserData->count--; - printf("[%012ld] Task:%p callback-> %s.\r\n", GetTick(), task, customUserData->str); + printf("[%012llu] Task:%p callback-> %s.\r\n", GetTick(), task, customUserData->str); if (customUserData->count > 0) { TaskStart(task); } } +#undef LOG_RUN_TIMER_FUN +#define LOG_RUN_TIMER_FUN GetTick() int Test_task(void *pVoid) { - TaskInit(GetTick); - TaskCreat(task1, 1000, -1, exampleTimer1Callback, "1000ms CYCLE task"); - TaskCreat(task2, 5000, -1, exampleTimer2Callback, "5000ms ONCE task"); - TaskCreat(task3, 3456, 2, exampleTimer3Callback, "3456ms delay start, 4567ms CYCLE task"); - CustomUserData customUserData = { - .count = 3, - .str = "2000ms 3 task" - }; - TaskCreat(task4, 2000, 1, exampleTimer4Callback, &customUserData); + LOG_RUN_TIME() { + TaskInit(GetTick); + TaskCreat(task1, 1000, -1, exampleTimer1Callback, "1000ms CYCLE task"); + TaskCreat(task2, 5000, -1, exampleTimer2Callback, "5000ms ONCE task"); + TaskCreat(task3, 3456, 2, exampleTimer3Callback, "3456ms delay start, 4567ms CYCLE task"); + CustomUserData customUserData = { + .count = 3, + .str = "2000ms 3 task" + }; + TaskCreat(task4, 2000, 1, exampleTimer4Callback, &customUserData); + } + + while (1) { TaskRun(); } diff --git a/demo/tft/test.c b/demo/tft/test.c index bd04f1e..e4cbd96 100644 --- a/demo/tft/test.c +++ b/demo/tft/test.c @@ -4,6 +4,8 @@ #include "t_tft.h" #include "sim_display.h" #include "tool.h" + +#define LOG_WITH_RUN_TIMER #include "log.h" TFT_T demo_tft; @@ -103,6 +105,10 @@ uint8_t tft_senddata(uint8_t *data, size_t len) { return result; } +int64_t GetSysCnt64() { + +} + int Test_tft(void *arg) { //设备信息预填充 demo_tft.width = 480;//实际如有支持不用填(如ST7735/7796) @@ -141,11 +147,11 @@ int Test_tft(void *arg) { TFT_ShowCHString(&demo_tft, 0, 60, "星海科技机械师", 1); - TFT_DrawCircle(&demo_tft, 25, 25, 10); - - + TFT_DrawCircle(&demo_tft, 25, 25, 15); + TFT_DrawRoundedRect(&demo_tft, 200, 0, 100, 30, 8); + TFT_DrawArc(&demo_tft, 200, 50, 30, 0, 360); TFT_DrawCross(&demo_tft, 25, 25, 10); - + TFT_DrawXCross(&demo_tft, 25, 25, 10); TFT_ShowString(&demo_tft, 60, 20, "JiXieShi", 16, 1); @@ -162,6 +168,7 @@ int Test_tft(void *arg) { // // Sleep(5); // } + SIM_Display_STOP(&tft_display); return 0; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e74ef06..730f525 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,6 +10,7 @@ set(LIBRARIES HW_LIB_Key key key/inc HW_LIB_Oled oled oled/inc HW_LIB_Tft tft tft/inc + HW_LIB_Flash flash flash/inc ) # 循环浏览库列表以创建它们 diff --git a/lib/flash/flash.cpp b/lib/flash/flash.cpp new file mode 100644 index 0000000..d9d9b1e --- /dev/null +++ b/lib/flash/flash.cpp @@ -0,0 +1,1038 @@ +#include "flash.h" +#include + +/* send dummy data for read data */ +#define DUMMY_DATA 0xFF + +#ifndef FLASH_FLASH_DEVICE_TABLE +#error "Please configure the flash device information table in (in flash_cfg.h)." +#endif + +/* user configured flash device information table */ +static FLASH_t flash_table[] = FLASH_FLASH_DEVICE_TABLE; +/* supported manufacturer information table */ +static const flash_mf mf_table[] = FLASH_MF_TABLE; + +#ifdef FLASH_USING_FLASH_INFO_TABLE +/* supported flash chip information table */ +static const flash_chip flash_chip_table[] = FLASH_FLASH_CHIP_TABLE; +#endif + +#ifdef FLASH_USING_QSPI +/** + * flash read data mode + */ +enum flash_qspi_read_mode { + NORMAL_SPI_READ = 1 << 0, /**< mormal spi read mode */ + DUAL_OUTPUT = 1 << 1, /**< qspi fast read dual output */ + DUAL_IO = 1 << 2, /**< qspi fast read dual input/output */ + QUAD_OUTPUT = 1 << 3, /**< qspi fast read quad output */ + QUAD_IO = 1 << 4, /**< qspi fast read quad input/output */ +}; + +/* QSPI flash chip's extended information table */ +static const flash_qspi_flash_ext_info qspi_flash_ext_info_table[] = FLASH_FLASH_EXT_INFO_TABLE; +#endif /* FLASH_USING_QSPI */ + +static flash_err software_init(const FLASH_t *flash); + +static flash_err hardware_init(FLASH_t *flash); + +static flash_err page256_or_1_byte_write(const FLASH_t *flash, uint32_t addr, size_t size, uint16_t write_gran, + const uint8_t *data); + +static flash_err aai_write(const FLASH_t *flash, uint32_t addr, size_t size, const uint8_t *data); + +static flash_err wait_busy(const FLASH_t *flash); + +static flash_err reset(const FLASH_t *flash); + +static flash_err read_jedec_id(FLASH_t *flash); + +static flash_err set_write_enabled(const FLASH_t *flash, bool enabled); + +static flash_err set_4_byte_address_mode(FLASH_t *flash, bool enabled); + +static void make_address_byte_array(const FLASH_t *flash, uint32_t addr, uint8_t *array); + +/* ../port/sfup_port.c */ +extern void flash_log_debug(const char *file, const long line, const char *format, ...); + +extern void flash_log_info(const char *format, ...); + +/** + * FLASH initialize by flash device + * + * @param flash flash device + * + * @return result + */ +flash_err flash_device_init(FLASH_t *flash) { + flash_err result = FLASH_SUCCESS; + + /* hardware initialize */ + result = hardware_init(flash); + if (result == FLASH_SUCCESS) { + result = software_init(flash); + } + if (result == FLASH_SUCCESS) { + flash->init_ok = true; + FLASH_INFO("%s flash device initialized successfully.", flash->name); + } else { + flash->init_ok = false; + FLASH_INFO("Error: %s flash device initialization failed.", flash->name); + } + + return result; +} + +/** + * FLASH library initialize. + * + * @return result + */ +flash_err flash_init(void) { + flash_err cur_flash_result = FLASH_SUCCESS, all_flash_result = FLASH_SUCCESS; + size_t i; + + FLASH_DEBUG("Start initialize Serial Flash Universal Driver(FLASH) V%s.", FLASH_SW_VERSION); + FLASH_DEBUG("You can get the latest version on https://github.com/armink/FLASH ."); + /* initialize all flash device in flash device table */ + for (i = 0; i < sizeof(flash_table) / sizeof(FLASH_t); i++) { + /* initialize flash device index of flash device information table */ + flash_table[i].index = i; + cur_flash_result = flash_device_init(&flash_table[i]); + + if (cur_flash_result != FLASH_SUCCESS) { + all_flash_result = cur_flash_result; + } + } + + return all_flash_result; +} + +/** + * get flash device by its index which in the flash information table + * + * @param index the index which in the flash information table @see flash_table + * + * @return flash device + */ +FLASH_t *flash_get_device(size_t index) { + if (index < flash_get_device_num()) { + return &flash_table[index]; + } else { + return NULL; + } +} + +/** + * get flash device total number on flash device information table @see flash_table + * + * @return flash device total number + */ +size_t flash_get_device_num(void) { + return sizeof(flash_table) / sizeof(FLASH_t); +} + +/** + * get flash device information table @see flash_table + * + * @return flash device table pointer + */ +const FLASH_t *flash_get_device_table(void) { + return flash_table; +} + +#ifdef FLASH_USING_QSPI + +static void qspi_set_read_cmd_format(FLASH_t *flash, uint8_t ins, uint8_t ins_lines, uint8_t addr_lines, + uint8_t dummy_cycles, uint8_t data_lines) { + /* if medium size greater than 16Mb, use 4-Byte address, instruction should be added one */ + if (flash->chip.capacity <= 0x1000000) { + flash->read_cmd_format.instruction = ins; + flash->read_cmd_format.address_size = 24; + } else { + if (ins == FLASH_CMD_READ_DATA) { + flash->read_cmd_format.instruction = ins + 0x10; + } else { + flash->read_cmd_format.instruction = ins + 1; + } + flash->read_cmd_format.address_size = 32; + } + + flash->read_cmd_format.instruction_lines = ins_lines; + flash->read_cmd_format.address_lines = addr_lines; + flash->read_cmd_format.alternate_bytes_lines = 0; + flash->read_cmd_format.dummy_cycles = dummy_cycles; + flash->read_cmd_format.data_lines = data_lines; +} + +/** + * Enbale the fast read mode in QSPI flash mode. Default read mode is normal SPI mode. + * + * it will find the appropriate fast-read instruction to replace the read instruction(0x03) + * fast-read instruction @see FLASH_FLASH_EXT_INFO_TABLE + * + * @note When Flash is in QSPI mode, the method must be called after flash_device_init(). + * + * @param flash flash device + * @param data_line_width the data lines max width which QSPI bus supported, such as 1, 2, 4 + * + * @return result + */ +flash_err flash_qspi_fast_read_enable(FLASH_t *flash, uint8_t data_line_width) { + size_t i = 0; + uint8_t read_mode = NORMAL_SPI_READ; + flash_err result = FLASH_SUCCESS; + + FLASH_ASSERT(flash); + FLASH_ASSERT(data_line_width == 1 || data_line_width == 2 || data_line_width == 4); + + /* get read_mode, If don't found, the default is FLASH_QSPI_NORMAL_SPI_READ */ + for (i = 0; i < sizeof(qspi_flash_ext_info_table) / sizeof(flash_qspi_flash_ext_info); i++) { + if ((qspi_flash_ext_info_table[i].mf_id == flash->chip.mf_id) + && (qspi_flash_ext_info_table[i].type_id == flash->chip.type_id) + && (qspi_flash_ext_info_table[i].capacity_id == flash->chip.capacity_id)) { + read_mode = qspi_flash_ext_info_table[i].read_mode; + } + } + + /* determine qspi supports which read mode and set read_cmd_format struct */ + switch (data_line_width) { + case 1: + qspi_set_read_cmd_format(flash, FLASH_CMD_READ_DATA, 1, 1, 0, 1); + break; + case 2: + if (read_mode & DUAL_IO) { + qspi_set_read_cmd_format(flash, FLASH_CMD_DUAL_IO_READ_DATA, 1, 2, 4, 2); + } else if (read_mode & DUAL_OUTPUT) { + qspi_set_read_cmd_format(flash, FLASH_CMD_DUAL_OUTPUT_READ_DATA, 1, 1, 8, 2); + } else { + qspi_set_read_cmd_format(flash, FLASH_CMD_READ_DATA, 1, 1, 0, 1); + } + break; + case 4: + if (read_mode & QUAD_IO) { + qspi_set_read_cmd_format(flash, FLASH_CMD_QUAD_IO_READ_DATA, 1, 4, 6, 4); + } else if (read_mode & QUAD_OUTPUT) { + qspi_set_read_cmd_format(flash, FLASH_CMD_QUAD_OUTPUT_READ_DATA, 1, 1, 8, 4); + } else { + qspi_set_read_cmd_format(flash, FLASH_CMD_READ_DATA, 1, 1, 0, 1); + } + break; + } + + return result; +} + +#endif /* FLASH_USING_QSPI */ + +/** + * hardware initialize + */ +static flash_err hardware_init(FLASH_t *flash) { + extern flash_err flash_spi_port_init(FLASH_t *flash); + + flash_err result = FLASH_SUCCESS; + size_t i; + + FLASH_ASSERT(flash); + + result = flash_spi_port_init(flash); + if (result != FLASH_SUCCESS) { + return result; + } + +#ifdef FLASH_USING_QSPI + /* set default read instruction */ + flash->read_cmd_format.instruction = FLASH_CMD_READ_DATA; +#endif /* FLASH_USING_QSPI */ + + /* SPI write read function must be initialize */ + FLASH_ASSERT(flash->spi.wr); + /* if the user don't configure flash chip information then using SFDP parameter or static flash parameter table */ + if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0 + || flash->chip.erase_gran_cmd == 0) { + /* read JEDEC ID include manufacturer ID, memory type ID and flash capacity ID */ + result = read_jedec_id(flash); + if (result != FLASH_SUCCESS) { + return result; + } + +#ifdef FLASH_USING_SFDP + extern bool flash_read_sfdp(FLASH_t *flash); + /* read SFDP parameters */ + if (flash_read_sfdp(flash)) { + flash->chip.name = NULL; + flash->chip.capacity = flash->sfdp.capacity; + /* only 1 byte or 256 bytes write mode for SFDP */ + if (flash->sfdp.write_gran == 1) { + flash->chip.write_mode = FLASH_WM_BYTE; + } else { + flash->chip.write_mode = FLASH_WM_PAGE_256B; + } + /* find the the smallest erase sector size for eraser. then will use this size for erase granularity */ + flash->chip.erase_gran = flash->sfdp.eraser[0].size; + flash->chip.erase_gran_cmd = flash->sfdp.eraser[0].cmd; + for (i = 1; i < FLASH_SFDP_ERASE_TYPE_MAX_NUM; i++) { + if (flash->sfdp.eraser[i].size != 0 && flash->chip.erase_gran > flash->sfdp.eraser[i].size) { + flash->chip.erase_gran = flash->sfdp.eraser[i].size; + flash->chip.erase_gran_cmd = flash->sfdp.eraser[i].cmd; + } + } + } else { +#endif + +#ifdef FLASH_USING_FLASH_INFO_TABLE + /* read SFDP parameters failed then using FLASH library provided static parameter */ + for (i = 0; i < sizeof(flash_chip_table) / sizeof(flash_chip); i++) { + if ((flash_chip_table[i].mf_id == flash->chip.mf_id) + && (flash_chip_table[i].type_id == flash->chip.type_id) + && (flash_chip_table[i].capacity_id == flash->chip.capacity_id)) { + flash->chip.name = flash_chip_table[i].name; + flash->chip.capacity = flash_chip_table[i].capacity; + flash->chip.write_mode = flash_chip_table[i].write_mode; + flash->chip.erase_gran = flash_chip_table[i].erase_gran; + flash->chip.erase_gran_cmd = flash_chip_table[i].erase_gran_cmd; + break; + } + } +#endif + +#ifdef FLASH_USING_SFDP + } +#endif + + } + + if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0 + || flash->chip.erase_gran_cmd == 0) { + FLASH_INFO("Warning: This flash device is not found or not supported."); + return FLASH_ERR_NOT_FOUND; + } else { + const char *flash_mf_name = NULL; + /* find the manufacturer information */ + for (i = 0; i < sizeof(mf_table) / sizeof(flash_mf); i++) { + if (mf_table[i].id == flash->chip.mf_id) { + flash_mf_name = mf_table[i].name; + break; + } + } + /* print manufacturer and flash chip name */ + if (flash_mf_name && flash->chip.name) { + FLASH_INFO("Found a %s %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.name, + flash->chip.capacity); + } else if (flash_mf_name) { + FLASH_INFO("Found a %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.capacity); + } else { + FLASH_INFO("Found a flash chip. Size is %ld bytes.", flash->chip.capacity); + } + } + + /* reset flash device */ + result = reset(flash); + if (result != FLASH_SUCCESS) { + return result; + } + + /* The flash all blocks is protected,so need change the flash status to unprotected before write and erase operate. */ + if (flash->chip.write_mode & FLASH_WM_AAI) { + result = flash_write_status(flash, true, 0x00); + } else { + /* MX25L3206E */ + if ((0xC2 == flash->chip.mf_id) && (0x20 == flash->chip.type_id) && (0x16 == flash->chip.capacity_id)) { + result = flash_write_status(flash, false, 0x00); + } + } + if (result != FLASH_SUCCESS) { + return result; + } + + /* if the flash is large than 16MB (256Mb) then enter in 4-Byte addressing mode */ + if (flash->chip.capacity > (1L << 24)) { + result = set_4_byte_address_mode(flash, true); + } else { + flash->addr_in_4_byte = false; + } + + return result; +} + +/** + * software initialize + * + * @param flash flash device + * + * @return result + */ +static flash_err software_init(const FLASH_t *flash) { + flash_err result = FLASH_SUCCESS; + + FLASH_ASSERT(flash); + + return result; +} + +/** + * read flash data + * + * @param flash flash device + * @param addr start address + * @param size read size + * @param data read data pointer + * + * @return result + */ +flash_err flash_read(const FLASH_t *flash, uint32_t addr, size_t size, uint8_t *data) { + flash_err result = FLASH_SUCCESS; + const flash_spi *spi = &flash->spi; + uint8_t cmd_data[5 + FLASH_READ_DUMMY_BYTE_CNT]; + uint8_t cmd_size; + uint8_t i; + + FLASH_ASSERT(flash); + FLASH_ASSERT(data); + /* must be call this function after initialize OK */ + FLASH_ASSERT(flash->init_ok); + /* check the flash address bound */ + if (addr + size > flash->chip.capacity) { + FLASH_INFO("Error: Flash address is out of bound."); + return FLASH_ERR_ADDR_OUT_OF_BOUND; + } + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + result = wait_busy(flash); + + if (result == FLASH_SUCCESS) { +#ifdef FLASH_USING_QSPI + if (flash->read_cmd_format.instruction != FLASH_CMD_READ_DATA) { + result = spi->qspi_read(spi, addr, (flash_qspi_read_cmd_format *) &flash->read_cmd_format, data, size); + } else +#endif + { +#ifdef FLASH_USING_FAST_READ + cmd_data[0] = FLASH_CMD_FAST_READ_DATA; +#else + cmd_data[0] = FLASH_CMD_READ_DATA; +#endif + make_address_byte_array(flash, addr, &cmd_data[1]); + cmd_size = flash->addr_in_4_byte ? 5 : 4; + for (i = 0; i < FLASH_READ_DUMMY_BYTE_CNT; i++) { + cmd_data[cmd_size] = FLASH_DUMMY_DATA; + cmd_size++; + } + result = spi->wr(spi, cmd_data, cmd_size, data, size); + } + } + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * erase all flash data + * + * @param flash flash device + * + * @return result + */ +flash_err flash_chip_erase(const FLASH_t *flash) { + flash_err result = FLASH_SUCCESS; + const flash_spi *spi = &flash->spi; + uint8_t cmd_data[4]; + + FLASH_ASSERT(flash); + /* must be call this function after initialize OK */ + FLASH_ASSERT(flash->init_ok); + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != FLASH_SUCCESS) { + goto __exit; + } + + cmd_data[0] = FLASH_CMD_ERASE_CHIP; + /* dual-buffer write, like AT45DB series flash chip erase operate is different for other flash */ + if (flash->chip.write_mode & FLASH_WM_DUAL_BUFFER) { + cmd_data[1] = 0x94; + cmd_data[2] = 0x80; + cmd_data[3] = 0x9A; + result = spi->wr(spi, cmd_data, 4, NULL, 0); + } else { + result = spi->wr(spi, cmd_data, 1, NULL, 0); + } + if (result != FLASH_SUCCESS) { + FLASH_INFO("Error: Flash chip erase SPI communicate error."); + goto __exit; + } + result = wait_busy(flash); + + __exit: + /* set the flash write disable */ + set_write_enabled(flash, false); + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * erase flash data + * + * @note It will erase align by erase granularity. + * + * @param flash flash device + * @param addr start address + * @param size erase size + * + * @return result + */ +flash_err flash_erase(const FLASH_t *flash, uint32_t addr, size_t size) { + extern size_t flash_sfdp_get_suitable_eraser(const FLASH_t *flash, uint32_t addr, size_t erase_size); + + flash_err result = FLASH_SUCCESS; + const flash_spi *spi = &flash->spi; + uint8_t cmd_data[5], cmd_size, cur_erase_cmd; + size_t cur_erase_size; + + FLASH_ASSERT(flash); + /* must be call this function after initialize OK */ + FLASH_ASSERT(flash->init_ok); + /* check the flash address bound */ + if (addr + size > flash->chip.capacity) { + FLASH_INFO("Error: Flash address is out of bound."); + return FLASH_ERR_ADDR_OUT_OF_BOUND; + } + + if (addr == 0 && size == flash->chip.capacity) { + return flash_chip_erase(flash); + } + + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + /* loop erase operate. erase unit is erase granularity */ + while (size) { + /* if this flash is support SFDP parameter, then used SFDP parameter supplies eraser */ +#ifdef FLASH_USING_SFDP + size_t eraser_index; + if (flash->sfdp.available) { + /* get the suitable eraser for erase process from SFDP parameter */ + eraser_index = flash_sfdp_get_suitable_eraser(flash, addr, size); + cur_erase_cmd = flash->sfdp.eraser[eraser_index].cmd; + cur_erase_size = flash->sfdp.eraser[eraser_index].size; + } else { +#else + { +#endif + cur_erase_cmd = flash->chip.erase_gran_cmd; + cur_erase_size = flash->chip.erase_gran; + } + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != FLASH_SUCCESS) { + goto __exit; + } + + cmd_data[0] = cur_erase_cmd; + make_address_byte_array(flash, addr, &cmd_data[1]); + cmd_size = flash->addr_in_4_byte ? 5 : 4; + result = spi->wr(spi, cmd_data, cmd_size, NULL, 0); + if (result != FLASH_SUCCESS) { + FLASH_INFO("Error: Flash erase SPI communicate error."); + goto __exit; + } + result = wait_busy(flash); + if (result != FLASH_SUCCESS) { + goto __exit; + } + /* make erase align and calculate next erase address */ + if (addr % cur_erase_size != 0) { + if (size > cur_erase_size - (addr % cur_erase_size)) { + size -= cur_erase_size - (addr % cur_erase_size); + addr += cur_erase_size - (addr % cur_erase_size); + } else { + goto __exit; + } + } else { + if (size > cur_erase_size) { + size -= cur_erase_size; + addr += cur_erase_size; + } else { + goto __exit; + } + } + } + + __exit: + /* set the flash write disable */ + set_write_enabled(flash, false); + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * write flash data (no erase operate) for write 1 to 256 bytes per page mode or byte write mode + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param write_gran write granularity bytes, only support 1 or 256 + * @param data write data + * + * @return result + */ +static flash_err page256_or_1_byte_write(const FLASH_t *flash, uint32_t addr, size_t size, uint16_t write_gran, + const uint8_t *data) { + flash_err result = FLASH_SUCCESS; + const flash_spi *spi = &flash->spi; + static uint8_t cmd_data[5 + FLASH_WRITE_MAX_PAGE_SIZE]; + uint8_t cmd_size; + size_t data_size; + + FLASH_ASSERT(flash); + /* only support 1 or 256 */ + FLASH_ASSERT(write_gran == 1 || write_gran == 256); + /* must be call this function after initialize OK */ + FLASH_ASSERT(flash->init_ok); + /* check the flash address bound */ + if (addr + size > flash->chip.capacity) { + FLASH_INFO("Error: Flash address is out of bound."); + return FLASH_ERR_ADDR_OUT_OF_BOUND; + } + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + /* loop write operate. write unit is write granularity */ + while (size) { + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != FLASH_SUCCESS) { + goto __exit; + } + cmd_data[0] = FLASH_CMD_PAGE_PROGRAM; + make_address_byte_array(flash, addr, &cmd_data[1]); + cmd_size = flash->addr_in_4_byte ? 5 : 4; + + /* make write align and calculate next write address */ + if (addr % write_gran != 0) { + if (size > write_gran - (addr % write_gran)) { + data_size = write_gran - (addr % write_gran); + } else { + data_size = size; + } + } else { + if (size > write_gran) { + data_size = write_gran; + } else { + data_size = size; + } + } + size -= data_size; + addr += data_size; + + memcpy(&cmd_data[cmd_size], data, data_size); + + result = spi->wr(spi, cmd_data, cmd_size + data_size, NULL, 0); + if (result != FLASH_SUCCESS) { + FLASH_INFO("Error: Flash write SPI communicate error."); + goto __exit; + } + result = wait_busy(flash); + if (result != FLASH_SUCCESS) { + goto __exit; + } + data += data_size; + } + + __exit: + /* set the flash write disable */ + set_write_enabled(flash, false); + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * write flash data (no erase operate) for auto address increment mode + * + * If the address is odd number, it will place one 0xFF before the start of data for protect the old data. + * If the latest remain size is 1, it will append one 0xFF at the end of data for protect the old data. + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param data write data + * + * @return result + */ +static flash_err aai_write(const FLASH_t *flash, uint32_t addr, size_t size, const uint8_t *data) { + flash_err result = FLASH_SUCCESS; + const flash_spi *spi = &flash->spi; + uint8_t cmd_data[8], cmd_size; + bool first_write = true; + + FLASH_ASSERT(flash); + FLASH_ASSERT(flash->init_ok); + /* check the flash address bound */ + if (addr + size > flash->chip.capacity) { + FLASH_INFO("Error: Flash address is out of bound."); + return FLASH_ERR_ADDR_OUT_OF_BOUND; + } + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + /* The address must be even for AAI write mode. So it must write one byte first when address is odd. */ + if (addr % 2 != 0) { + result = page256_or_1_byte_write(flash, addr++, 1, 1, data++); + if (result != FLASH_SUCCESS) { + goto __exit; + } + size--; + } + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != FLASH_SUCCESS) { + goto __exit; + } + /* loop write operate. */ + cmd_data[0] = FLASH_CMD_AAI_WORD_PROGRAM; + while (size >= 2) { + if (first_write) { + make_address_byte_array(flash, addr, &cmd_data[1]); + cmd_size = flash->addr_in_4_byte ? 5 : 4; + cmd_data[cmd_size] = *data; + cmd_data[cmd_size + 1] = *(data + 1); + first_write = false; + } else { + cmd_size = 1; + cmd_data[1] = *data; + cmd_data[2] = *(data + 1); + } + + result = spi->wr(spi, cmd_data, cmd_size + 2, NULL, 0); + if (result != FLASH_SUCCESS) { + FLASH_INFO("Error: Flash write SPI communicate error."); + goto __exit; + } + + result = wait_busy(flash); + if (result != FLASH_SUCCESS) { + goto __exit; + } + + size -= 2; + addr += 2; + data += 2; + } + /* set the flash write disable for exit AAI mode */ + result = set_write_enabled(flash, false); + /* write last one byte data when origin write size is odd */ + if (result == FLASH_SUCCESS && size == 1) { + result = page256_or_1_byte_write(flash, addr, 1, 1, data); + } + + __exit: + if (result != FLASH_SUCCESS) { + set_write_enabled(flash, false); + } + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * write flash data (no erase operate) + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param data write data + * + * @return result + */ +flash_err flash_write(const FLASH_t *flash, uint32_t addr, size_t size, const uint8_t *data) { + flash_err result = FLASH_SUCCESS; + + if (flash->chip.write_mode & FLASH_WM_PAGE_256B) { + result = page256_or_1_byte_write(flash, addr, size, 256, data); + } else if (flash->chip.write_mode & FLASH_WM_AAI) { + result = aai_write(flash, addr, size, data); + } else if (flash->chip.write_mode & FLASH_WM_DUAL_BUFFER) { + //TODO dual-buffer write mode + } + + return result; +} + +/** + * erase and write flash data + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param data write data + * + * @return result + */ +flash_err flash_erase_write(const FLASH_t *flash, uint32_t addr, size_t size, const uint8_t *data) { + flash_err result = FLASH_SUCCESS; + + result = flash_erase(flash, addr, size); + + if (result == FLASH_SUCCESS) { + result = flash_write(flash, addr, size, data); + } + + return result; +} + +static flash_err reset(const FLASH_t *flash) { + flash_err result = FLASH_SUCCESS; + const flash_spi *spi = &flash->spi; + uint8_t cmd_data[2]; + + FLASH_ASSERT(flash); + + cmd_data[0] = FLASH_CMD_ENABLE_RESET; + result = spi->wr(spi, cmd_data, 1, NULL, 0); + if (result == FLASH_SUCCESS) { + result = wait_busy(flash); + } else { + FLASH_INFO("Error: Flash device reset failed."); + return result; + } + + cmd_data[1] = FLASH_CMD_RESET; + result = spi->wr(spi, &cmd_data[1], 1, NULL, 0); + + if (result == FLASH_SUCCESS) { + result = wait_busy(flash); + } + + if (result == FLASH_SUCCESS) { + FLASH_DEBUG("Flash device reset success."); + } else { + FLASH_INFO("Error: Flash device reset failed."); + } + + return result; +} + +static flash_err read_jedec_id(FLASH_t *flash) { + flash_err result = FLASH_SUCCESS; + const flash_spi *spi = &flash->spi; + uint8_t cmd_data[1], recv_data[3]; + + FLASH_ASSERT(flash); + + cmd_data[0] = FLASH_CMD_JEDEC_ID; + result = spi->wr(spi, cmd_data, sizeof(cmd_data), recv_data, sizeof(recv_data)); + if (result == FLASH_SUCCESS) { + flash->chip.mf_id = recv_data[0]; + flash->chip.type_id = recv_data[1]; + flash->chip.capacity_id = recv_data[2]; + FLASH_DEBUG("The flash device manufacturer ID is 0x%02X, memory type ID is 0x%02X, capacity ID is 0x%02X.", + flash->chip.mf_id, flash->chip.type_id, flash->chip.capacity_id); + } else { + FLASH_INFO("Error: Read flash device JEDEC ID error."); + } + + return result; +} + +/** + * set the flash write enable or write disable + * + * @param flash flash device + * @param enabled true: enable false: disable + * + * @return result + */ +static flash_err set_write_enabled(const FLASH_t *flash, bool enabled) { + flash_err result = FLASH_SUCCESS; + uint8_t cmd, register_status; + + FLASH_ASSERT(flash); + + if (enabled) { + cmd = FLASH_CMD_WRITE_ENABLE; + } else { + cmd = FLASH_CMD_WRITE_DISABLE; + } + + result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0); + + if (result == FLASH_SUCCESS) { + result = flash_read_status(flash, ®ister_status); + } + + if (result == FLASH_SUCCESS) { + if (enabled && (register_status & FLASH_STATUS_REGISTER_WEL) == 0) { + FLASH_INFO("Error: Can't enable write status."); + return FLASH_ERR_WRITE; + } else if (!enabled && (register_status & FLASH_STATUS_REGISTER_WEL) != 0) { + FLASH_INFO("Error: Can't disable write status."); + return FLASH_ERR_WRITE; + } + } + + return result; +} + +/** + * enable or disable 4-Byte addressing for flash + * + * @note The 4-Byte addressing just supported for the flash capacity which is large then 16MB (256Mb). + * + * @param flash flash device + * @param enabled true: enable false: disable + * + * @return result + */ +static flash_err set_4_byte_address_mode(FLASH_t *flash, bool enabled) { + flash_err result = FLASH_SUCCESS; + uint8_t cmd; + + FLASH_ASSERT(flash); + + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != FLASH_SUCCESS) { + return result; + } + + if (enabled) { + cmd = FLASH_CMD_ENTER_4B_ADDRESS_MODE; + } else { + cmd = FLASH_CMD_EXIT_4B_ADDRESS_MODE; + } + + result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0); + + if (result == FLASH_SUCCESS) { + flash->addr_in_4_byte = enabled ? true : false; + FLASH_DEBUG("%s 4-Byte addressing mode success.", enabled ? "Enter" : "Exit"); + } else { + FLASH_INFO("Error: %s 4-Byte addressing mode failed.", enabled ? "Enter" : "Exit"); + } + + return result; +} + +/** + * read flash register status + * + * @param flash flash device + * @param status register status + * + * @return result + */ +flash_err flash_read_status(const FLASH_t *flash, uint8_t *status) { + uint8_t cmd = FLASH_CMD_READ_STATUS_REGISTER; + + FLASH_ASSERT(flash); + FLASH_ASSERT(status); + + return flash->spi.wr(&flash->spi, &cmd, 1, status, 1); +} + +static flash_err wait_busy(const FLASH_t *flash) { + flash_err result = FLASH_SUCCESS; + uint8_t status; + size_t retry_times = flash->retry.times; + + FLASH_ASSERT(flash); + + while (true) { + result = flash_read_status(flash, &status); + if (result == FLASH_SUCCESS && ((status & FLASH_STATUS_REGISTER_BUSY)) == 0) { + break; + } + /* retry counts */ + FLASH_RETRY_PROCESS(flash->retry.delay, retry_times, result); + } + + if (result != FLASH_SUCCESS || ((status & FLASH_STATUS_REGISTER_BUSY)) != 0) { + FLASH_INFO("Error: Flash wait busy has an error."); + } + + return result; +} + +static void make_address_byte_array(const FLASH_t *flash, uint32_t addr, uint8_t *array) { + uint8_t len, i; + + FLASH_ASSERT(flash); + FLASH_ASSERT(array); + + len = flash->addr_in_4_byte ? 4 : 3; + + for (i = 0; i < len; i++) { + array[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF; + } +} + +/** + * write status register + * + * @param flash flash device + * @param is_volatile true: volatile mode, false: non-volatile mode + * @param status register status + * + * @return result + */ +flash_err flash_write_status(const FLASH_t *flash, bool is_volatile, uint8_t status) { + flash_err result = FLASH_SUCCESS; + const flash_spi *spi = &flash->spi; + uint8_t cmd_data[2]; + + FLASH_ASSERT(flash); + + if (is_volatile) { + cmd_data[0] = FLASH_VOLATILE_SR_WRITE_ENABLE; + result = spi->wr(spi, cmd_data, 1, NULL, 0); + } else { + result = set_write_enabled(flash, true); + } + + if (result == FLASH_SUCCESS) { + cmd_data[0] = FLASH_CMD_WRITE_STATUS_REGISTER; + cmd_data[1] = status; + result = spi->wr(spi, cmd_data, 2, NULL, 0); + } + + if (result != FLASH_SUCCESS) { + FLASH_INFO("Error: Write_status register failed."); + } + + return result; +} diff --git a/lib/flash/flash_sfdp.cpp b/lib/flash/flash_sfdp.cpp new file mode 100644 index 0000000..1b6c4d3 --- /dev/null +++ b/lib/flash/flash_sfdp.cpp @@ -0,0 +1,359 @@ +#include "flash.h" + +/** + * JEDEC Standard JESD216 Terms and definitions: + * + * DWORD: Four consecutive 8-bit bytes used as the basic 32-bit building block for headers and parameter tables. + * + * Sector: The minimum granularity - size and alignment - of an area that can be erased in the data array + * of a flash memory device. Different areas within the address range of the data array may have a different + * minimum erase granularity (sector size). + */ + +#ifdef FLASH_USING_SFDP + +/* support maximum SFDP major revision by driver */ +#define SUPPORT_MAX_SFDP_MAJOR_REV 1 +/* the JEDEC basic flash parameter table length is 9 DWORDs (288-bit) on JESD216 (V1.0) initial release standard */ +#define BASIC_TABLE_LEN 9 +/* the smallest eraser in SFDP eraser table */ +#define SMALLEST_ERASER_INDEX 0 +/** + * SFDP parameter header structure + */ +typedef struct { + uint8_t id; /**< Parameter ID LSB */ + uint8_t minor_rev; /**< Parameter minor revision */ + uint8_t major_rev; /**< Parameter major revision */ + uint8_t len; /**< Parameter table length(in double words) */ + uint32_t ptp; /**< Parameter table 24bit pointer (byte address) */ +} sfdp_para_header; + +static flash_err read_sfdp_data(const FLASH_t *flash, uint32_t addr, uint8_t *read_buf, size_t size); + +static bool read_sfdp_header(FLASH_t *flash); + +static bool read_basic_header(const FLASH_t *flash, sfdp_para_header *basic_header); + +static bool read_basic_table(FLASH_t *flash, sfdp_para_header *basic_header); + +extern void flash_log_debug(const char *file, const long line, const char *format, ...); + +extern void flash_log_info(const char *format, ...); + +/** + * Read SFDP parameter information + * + * @param flash flash device + * + * @return true: read OK + */ +bool flash_read_sfdp(FLASH_t *flash) { + FLASH_ASSERT(flash); + + /* JEDEC basic flash parameter header */ + sfdp_para_header basic_header; + if (read_sfdp_header(flash) && read_basic_header(flash, &basic_header)) { + return read_basic_table(flash, &basic_header); + } else { + FLASH_INFO("Warning: Read SFDP parameter header information failed. The %s does not support JEDEC SFDP.", + flash->name); + return false; + } +} + +/** + * Read SFDP parameter header + * + * @param flash flash device + * + * @return true: read OK + */ +static bool read_sfdp_header(FLASH_t *flash) { + flash_sfdp *sfdp = &flash->sfdp; + /* The SFDP header is located at address 000000h of the SFDP data structure. + * It identifies the SFDP Signature, the number of parameter headers, and the SFDP revision numbers. */ + /* sfdp parameter header address */ + uint32_t header_addr = 0; + /* each parameter header being 2 DWORDs (64-bit) */ + uint8_t header[2 * 4] = {0}; + + FLASH_ASSERT(flash); + + sfdp->available = false; + /* read SFDP header */ + if (read_sfdp_data(flash, header_addr, header, sizeof(header)) != FLASH_SUCCESS) { + FLASH_INFO("Error: Can't read SFDP header."); + return false; + } + /* check SFDP header */ + if (!(header[0] == 'S' && + header[1] == 'F' && + header[2] == 'D' && + header[3] == 'P')) { + FLASH_DEBUG("Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P')."); + return false; + } + sfdp->minor_rev = header[4]; + sfdp->major_rev = header[5]; + if (sfdp->major_rev > SUPPORT_MAX_SFDP_MAJOR_REV) { + FLASH_INFO("Error: This reversion(V%d.%d) of SFDP is not supported.", sfdp->major_rev, sfdp->minor_rev); + return false; + } + FLASH_DEBUG("Check SFDP header is OK. The reversion is V%d.%d, NPN is %d.", sfdp->major_rev, sfdp->minor_rev, + header[6]); + + return true; +} + +/** + * Read JEDEC basic parameter header + * + * @param flash flash device + * + * @return true: read OK + */ +static bool read_basic_header(const FLASH_t *flash, sfdp_para_header *basic_header) { + /* The basic parameter header is mandatory, is defined by this standard, and starts at byte offset 08h. */ + uint32_t header_addr = 8; + /* each parameter header being 2 DWORDs (64-bit) */ + uint8_t header[2 * 4] = {0}; + + FLASH_ASSERT(flash); + FLASH_ASSERT(basic_header); + + /* read JEDEC basic flash parameter header */ + if (read_sfdp_data(flash, header_addr, header, sizeof(header)) != FLASH_SUCCESS) { + FLASH_INFO("Error: Can't read JEDEC basic flash parameter header."); + return false; + } + basic_header->id = header[0]; + basic_header->minor_rev = header[1]; + basic_header->major_rev = header[2]; + basic_header->len = header[3]; + basic_header->ptp = (long) header[4] | (long) header[5] << 8 | (long) header[6] << 16; + /* check JEDEC basic flash parameter header */ + if (basic_header->major_rev > SUPPORT_MAX_SFDP_MAJOR_REV) { + FLASH_INFO("Error: This reversion(V%d.%d) of JEDEC basic flash parameter header is not supported.", + basic_header->major_rev, basic_header->minor_rev); + return false; + } + if (basic_header->len < BASIC_TABLE_LEN) { + FLASH_INFO("Error: The JEDEC basic flash parameter table length (now is %d) error.", basic_header->len); + return false; + } + FLASH_DEBUG("Check JEDEC basic flash parameter header is OK. The table id is %d, reversion is V%d.%d," + " length is %d, parameter table pointer is 0x%06lX.", basic_header->id, basic_header->major_rev, + basic_header->minor_rev, basic_header->len, basic_header->ptp); + + return true; +} + +/** + * Read JEDEC basic parameter table + * + * @param flash flash device + * + * @return true: read OK + */ +static bool read_basic_table(FLASH_t *flash, sfdp_para_header *basic_header) { + flash_sfdp *sfdp = &flash->sfdp; + /* parameter table address */ + uint32_t table_addr = basic_header->ptp; + /* parameter table */ + uint8_t table[BASIC_TABLE_LEN * 4] = {0}, i, j; + + FLASH_ASSERT(flash); + FLASH_ASSERT(basic_header); + + /* read JEDEC basic flash parameter table */ + if (read_sfdp_data(flash, table_addr, table, sizeof(table)) != FLASH_SUCCESS) { + FLASH_INFO("Warning: Can't read JEDEC basic flash parameter table."); + return false; + } + /* print JEDEC basic flash parameter table info */ + FLASH_DEBUG("JEDEC basic flash parameter table info:"); + FLASH_DEBUG("MSB-LSB 3 2 1 0"); + for (i = 0; i < BASIC_TABLE_LEN; i++) { + FLASH_DEBUG("[%04d] 0x%02X 0x%02X 0x%02X 0x%02X", i + 1, table[i * 4 + 3], table[i * 4 + 2], table[i * 4 + 1], + table[i * 4]); + } + + /* get block/sector 4 KB erase supported and command */ + sfdp->erase_4k_cmd = table[1]; + switch (table[0] & 0x03) { + case 1: + sfdp->erase_4k = true; + FLASH_DEBUG("4 KB Erase is supported throughout the device. Command is 0x%02X.", sfdp->erase_4k_cmd); + break; + case 3: + sfdp->erase_4k = false; + FLASH_DEBUG("Uniform 4 KB erase is unavailable for this device."); + break; + default: + FLASH_INFO("Error: Uniform 4 KB erase supported information error."); + return false; + } + /* get write granularity */ + //TODO 目前为 1.0 所提供的方式,后期支持 V1.5 及以上的方式读取 page size + switch ((table[0] & (0x01 << 2)) >> 2) { + case 0: + sfdp->write_gran = 1; + FLASH_DEBUG("Write granularity is 1 byte."); + break; + case 1: + sfdp->write_gran = 256; + FLASH_DEBUG("Write granularity is 64 bytes or larger."); + break; + } + /* volatile status register block protect bits */ + switch ((table[0] & (0x01 << 3)) >> 3) { + case 0: + /* Block Protect bits in device's status register are solely non-volatile or may be + * programmed either as volatile using the 50h instruction for write enable or non-volatile + * using the 06h instruction for write enable. + */ + sfdp->sr_is_non_vola = true; + FLASH_DEBUG("Target flash status register is non-volatile."); + break; + case 1: + /* block protect bits in device's status register are solely volatile. */ + sfdp->sr_is_non_vola = false; + FLASH_DEBUG("Block Protect bits in device's status register are solely volatile."); + /* write enable instruction select for writing to volatile status register */ + switch ((table[0] & (0x01 << 4)) >> 4) { + case 0: + sfdp->vola_sr_we_cmd = FLASH_VOLATILE_SR_WRITE_ENABLE; + FLASH_DEBUG("Flash device requires instruction 50h as the write enable prior " + "to performing a volatile write to the status register."); + break; + case 1: + sfdp->vola_sr_we_cmd = FLASH_CMD_WRITE_ENABLE; + FLASH_DEBUG("Flash device requires instruction 06h as the write enable prior " + "to performing a volatile write to the status register."); + break; + } + break; + } + /* get address bytes, number of bytes used in addressing flash array read, write and erase. */ + switch ((table[2] & (0x03 << 1)) >> 1) { + case 0: + sfdp->addr_3_byte = true; + sfdp->addr_4_byte = false; + FLASH_DEBUG("3-Byte only addressing."); + break; + case 1: + sfdp->addr_3_byte = true; + sfdp->addr_4_byte = true; + FLASH_DEBUG("3- or 4-Byte addressing."); + break; + case 2: + sfdp->addr_3_byte = false; + sfdp->addr_4_byte = true; + FLASH_DEBUG("4-Byte only addressing."); + break; + default: + sfdp->addr_3_byte = false; + sfdp->addr_4_byte = false; + FLASH_INFO("Error: Read address bytes error!"); + return false; + } + /* get flash memory capacity */ + uint32_t table2_temp = ((long) table[7] << 24) | ((long) table[6] << 16) | ((long) table[5] << 8) | (long) table[4]; + switch ((table[7] & (0x01 << 7)) >> 7) { + case 0: + sfdp->capacity = 1 + (table2_temp >> 3); + break; + case 1: + table2_temp &= 0x7FFFFFFF; + if (table2_temp > sizeof(sfdp->capacity) * 8 + 3) { + sfdp->capacity = 0; + FLASH_INFO("Error: The flash capacity is grater than 32 Gb/ 4 GB! Not Supported."); + return false; + } + sfdp->capacity = 1L << (table2_temp - 3); + break; + } + FLASH_DEBUG("Capacity is %ld Bytes.", sfdp->capacity); + /* get erase size and erase command */ + for (i = 0, j = 0; i < FLASH_SFDP_ERASE_TYPE_MAX_NUM; i++) { + if (table[28 + 2 * i] != 0x00) { + sfdp->eraser[j].size = 1L << table[28 + 2 * i]; + sfdp->eraser[j].cmd = table[28 + 2 * i + 1]; + FLASH_DEBUG("Flash device supports %ldKB block erase. Command is 0x%02X.", sfdp->eraser[j].size / 1024, + sfdp->eraser[j].cmd); + j++; + } + } + /* sort the eraser size from small to large */ + for (i = 0, j = 0; i < FLASH_SFDP_ERASE_TYPE_MAX_NUM; i++) { + if (sfdp->eraser[i].size) { + for (j = i + 1; j < FLASH_SFDP_ERASE_TYPE_MAX_NUM; j++) { + if (sfdp->eraser[j].size != 0 && sfdp->eraser[i].size > sfdp->eraser[j].size) { + /* swap the small eraser */ + uint32_t temp_size = sfdp->eraser[i].size; + uint8_t temp_cmd = sfdp->eraser[i].cmd; + sfdp->eraser[i].size = sfdp->eraser[j].size; + sfdp->eraser[i].cmd = sfdp->eraser[j].cmd; + sfdp->eraser[j].size = temp_size; + sfdp->eraser[j].cmd = temp_cmd; + } + } + } + } + + sfdp->available = true; + return true; +} + +static flash_err read_sfdp_data(const FLASH_t *flash, uint32_t addr, uint8_t *read_buf, size_t size) { + uint8_t cmd[] = { + FLASH_CMD_READ_SFDP_REGISTER, + (uint8_t) ((addr >> 16) & 0xFF), + (uint8_t) ((addr >> 8) & 0xFF), + (uint8_t) ((addr >> 0) & 0xFF), + FLASH_DUMMY_DATA, + }; + + FLASH_ASSERT(flash); + FLASH_ASSERT(addr < 1L << 24); + FLASH_ASSERT(read_buf); + FLASH_ASSERT(flash->spi.wr); + + return flash->spi.wr(&flash->spi, cmd, sizeof(cmd), read_buf, size); +} + +/** + * get the most suitable eraser for erase process from SFDP parameter + * + * @param flash flash device + * @param addr start address + * @param erase_size will be erased size + * + * @return the eraser index of SFDP eraser table @see flash_sfdp.eraser[] + */ +size_t flash_sfdp_get_suitable_eraser(const FLASH_t *flash, uint32_t addr, size_t erase_size) { + size_t index = SMALLEST_ERASER_INDEX, i; + /* only used when flash supported SFDP */ + FLASH_ASSERT(flash->sfdp.available); + /* the address isn't align by smallest eraser's size, then use the smallest eraser */ + if (addr % flash->sfdp.eraser[SMALLEST_ERASER_INDEX].size) { + return SMALLEST_ERASER_INDEX; + } + /* Find the suitable eraser. + * The largest size eraser is at the end of eraser table. + * In order to decrease erase command counts, so the find process is from the end of eraser table. */ + for (i = FLASH_SFDP_ERASE_TYPE_MAX_NUM - 1;; i--) { + if ((flash->sfdp.eraser[i].size != 0) && (erase_size >= flash->sfdp.eraser[i].size) + && (addr % flash->sfdp.eraser[i].size == 0)) { + index = i; + break; + } + if (i == SMALLEST_ERASER_INDEX) { + break; + } + } + return index; +} + +#endif /* FLASH_USING_SFDP */ diff --git a/lib/flash/inc/flash.h b/lib/flash/inc/flash.h new file mode 100644 index 0000000..45509e6 --- /dev/null +++ b/lib/flash/inc/flash.h @@ -0,0 +1,208 @@ +// +// Created by lydxh on 24-11-27. +// + +#ifndef HW_LIB_FLASH_H +#define HW_LIB_FLASH_H + +#include +#include +#include +#include +#include +#include "flash_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* debug print function. Must be implement by user. */ +#ifdef FLASH_DEBUG_MODE +#ifndef FLASH_DEBUG +#define FLASH_DEBUG(...) flash_log_debug(__FILE__, __LINE__, __VA_ARGS__) +#endif /* FLASH_DEBUG */ +#else +#define FLASH_DEBUG(...) +#endif /* FLASH_DEBUG_MODE */ + +#ifndef FLASH_INFO +#define FLASH_INFO(...) flash_log_info(__VA_ARGS__) +#endif + +/* assert for developer. */ +#ifdef FLASH_DEBUG_MODE +#define FLASH_ASSERT(EXPR) \ +if (!(EXPR)) \ +{ \ + FLASH_DEBUG("(%s) has assert failed at %s.", #EXPR, __FUNCTION__); \ + while (1); \ +} +#else +#define FLASH_ASSERT(EXPR) +#endif + +typedef struct FLASH_Dev FLASH_t; + +struct FLASH_Dev { + char *name; /**< serial flash name */ + size_t index; /**< index of flash device information table @see flash_table */ + flash_chip chip; /**< flash chip information */ + flash_spi spi; /**< SPI device */ + bool init_ok; /**< initialize OK flag */ + bool addr_in_4_byte; /**< flash is in 4-Byte addressing */ + struct { + void (*delay)(void); /**< every retry's delay */ + size_t times; /**< default times for error retry */ + } retry; + + void *user_data; /**< some user data */ + +#ifdef FLASH_USING_QSPI + flash_qspi_read_cmd_format read_cmd_format; /**< fast read cmd format */ +#endif + +#ifdef FLASH_USING_SFDP + flash_sfdp sfdp; /**< serial flash discoverable parameters by JEDEC standard */ +#endif +}; + + +/** + * FLASH library initialize. + * + * @return result + */ +flash_err flash_init(void); + +/** + * FLASH initialize by flash device + * + * @param flash flash device + * + * @return result + */ +flash_err flash_device_init(FLASH_t *flash); + +/** + * get flash device by its index which in the flash information table + * + * @param index the index which in the flash information table @see flash_table + * + * @return flash device + */ +FLASH_t *flash_get_device(size_t index); + +/** + * get flash device total number on flash device information table @see flash_table + * + * @return flash device total number + */ +size_t flash_get_device_num(void); + +/** + * get flash device information table @see flash_table + * + * @return flash device table pointer + */ +const FLASH_t *flash_get_device_table(void); + +#ifdef FLASH_USING_QSPI +/** + * Enbale the fast read mode in QSPI flash mode. Default read mode is normal SPI mode. + * + * it will find the appropriate fast-read instruction to replace the read instruction(0x03) + * fast-read instruction @see FLASH_FLASH_EXT_INFO_TABLE + * + * @note When Flash is in QSPI mode, the method must be called after flash_device_init(). + * + * @param flash flash device + * @param data_line_width the data lines max width which QSPI bus supported, such as 1, 2, 4 + * + * @return result + */ +flash_err flash_qspi_fast_read_enable(FLASH_t *flash, uint8_t data_line_width); +#endif /* FLASH_USING_QSPI */ + +/** + * read flash data + * + * @param flash flash device + * @param addr start address + * @param size read size + * @param data read data pointer + * + * @return result + */ +flash_err flash_read(const FLASH_t *flash, uint32_t addr, size_t size, uint8_t *data); + +/** + * erase flash data + * + * @note It will erase align by erase granularity. + * + * @param flash flash device + * @param addr start address + * @param size erase size + * + * @return result + */ +flash_err flash_erase(const FLASH_t *flash, uint32_t addr, size_t size); + +/** + * write flash data (no erase operate) + * + * @param flash flash device + * @param addr start address + * @param data write data + * @param size write size + * + * @return result + */ +flash_err flash_write(const FLASH_t *flash, uint32_t addr, size_t size, const uint8_t *data); + +/** + * erase and write flash data + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param data write data + * + * @return result + */ +flash_err flash_erase_write(const FLASH_t *flash, uint32_t addr, size_t size, const uint8_t *data); + +/** + * erase all flash data + * + * @param flash flash device + * + * @return result + */ +flash_err flash_chip_erase(const FLASH_t *flash); + +/** + * read flash register status + * + * @param flash flash device + * @param status register status + * + * @return result + */ +flash_err flash_read_status(const FLASH_t *flash, uint8_t *status); + +/** + * write status register + * + * @param flash flash device + * @param is_volatile true: volatile mode, false: non-volatile mode + * @param status register status + * + * @return result + */ +flash_err flash_write_status(const FLASH_t *flash, bool is_volatile, uint8_t status); + +#ifdef __cplusplus +} +#endif +#endif //HW_LIB_FLASH_H diff --git a/lib/flash/inc/flash_cfg.h b/lib/flash/inc/flash_cfg.h new file mode 100644 index 0000000..f7784a8 --- /dev/null +++ b/lib/flash/inc/flash_cfg.h @@ -0,0 +1,27 @@ +// +// Created by lydxh on 24-11-27. +// + +#ifndef HW_LIB_FLASH_CFG_H +#define HW_LIB_FLASH_CFG_H + +#define FLASH_DEBUG_MODE + +#define FLASH_USING_SFDP + +// #define FLASH_USING_FAST_READ + +#define FLASH_USING_FLASH_INFO_TABLE + +enum { + FLASH_XXXX_DEVICE_INDEX = 0, +}; + +#define FLASH_FLASH_DEVICE_TABLE \ +{ \ + [FLASH_XXXX_DEVICE_INDEX] = {.name = "XXXX", .spi.name = "SPIX"}, \ +} + +#define FLASH_USING_QSPI + +#endif //HW_LIB_FLASH_CFG_H diff --git a/lib/flash/inc/flash_def.h b/lib/flash/inc/flash_def.h new file mode 100644 index 0000000..d619b81 --- /dev/null +++ b/lib/flash/inc/flash_def.h @@ -0,0 +1,406 @@ +// +// Created by lydxh on 24-11-27. +// + +#ifndef HW_LIB_FLASH_DEF_H +#define HW_LIB_FLASH_DEF_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * flash program(write) data mode + */ +enum flash_write_mode { + FLASH_WM_PAGE_256B = 1 << 0, /**< write 1 to 256 bytes per page */ + FLASH_WM_BYTE = 1 << 1, /**< byte write */ + FLASH_WM_AAI = 1 << 2, /**< auto address increment */ + FLASH_WM_DUAL_BUFFER = 1 << 3, /**< dual-buffer write, like AT45DB series */ +}; + +/* manufacturer information */ +typedef struct { + char *name; + uint8_t id; +} flash_mf; + +/* flash chip information */ +typedef struct { + char *name; /**< flash chip name */ + uint8_t mf_id; /**< manufacturer ID */ + uint8_t type_id; /**< memory type ID */ + uint8_t capacity_id; /**< capacity ID */ + uint32_t capacity; /**< flash capacity (bytes) */ + uint16_t write_mode; /**< write mode @see flash_write_mode */ + uint32_t erase_gran; /**< erase granularity (bytes) */ + uint8_t erase_gran_cmd; /**< erase granularity size block command */ +} flash_chip; + +#ifdef FLASH_USING_QSPI +/* QSPI flash chip's extended information compared with SPI flash */ +typedef struct { + uint8_t mf_id; /**< manufacturer ID */ + uint8_t type_id; /**< memory type ID */ + uint8_t capacity_id; /**< capacity ID */ + uint8_t read_mode; /**< supported read mode on this qspi flash chip */ +} flash_qspi_flash_ext_info; +#endif + +/* FLASH support manufacturer JEDEC ID */ +#define FLASH_MF_ID_CYPRESS 0x01 +#define FLASH_MF_ID_FUJITSU 0x04 +#define FLASH_MF_ID_EON 0x1C +#define FLASH_MF_ID_ATMEL 0x1F +#define FLASH_MF_ID_MICRON 0x20 +#define FLASH_MF_ID_AMIC 0x37 +#define FLASH_MF_ID_NOR_MEM 0x52 +#define FLASH_MF_ID_SANYO 0x62 +#define FLASH_MF_ID_INTEL 0x89 +#define FLASH_MF_ID_ESMT 0x8C +#define FLASH_MF_ID_FUDAN 0xA1 +#define FLASH_MF_ID_HYUNDAI 0xAD +#define FLASH_MF_ID_SST 0xBF +#define FLASH_MF_ID_MACRONIX 0xC2 +#define FLASH_MF_ID_GIGADEVICE 0xC8 +#define FLASH_MF_ID_ISSI 0xD5 +#define FLASH_MF_ID_WINBOND 0xEF +#define FLASH_MF_ID_PUYA 0x85 + +/* FLASH supported manufacturer information table */ +#define FLASH_MF_TABLE \ +{ \ + {"Cypress", FLASH_MF_ID_CYPRESS}, \ + {"Fujitsu", FLASH_MF_ID_FUJITSU}, \ + {"EON", FLASH_MF_ID_EON}, \ + {"Atmel", FLASH_MF_ID_ATMEL}, \ + {"Micron", FLASH_MF_ID_MICRON}, \ + {"AMIC", FLASH_MF_ID_AMIC}, \ + {"Sanyo", FLASH_MF_ID_SANYO}, \ + {"Intel", FLASH_MF_ID_INTEL}, \ + {"ESMT", FLASH_MF_ID_ESMT}, \ + {"Fudan", FLASH_MF_ID_FUDAN}, \ + {"Hyundai", FLASH_MF_ID_HYUNDAI}, \ + {"SST", FLASH_MF_ID_SST}, \ + {"GigaDevice", FLASH_MF_ID_GIGADEVICE}, \ + {"ISSI", FLASH_MF_ID_ISSI}, \ + {"Winbond", FLASH_MF_ID_WINBOND}, \ + {"Macronix", FLASH_MF_ID_MACRONIX}, \ + {"NOR-MEM", FLASH_MF_ID_NOR_MEM}, \ + {"PUYA", FLASH_MF_ID_PUYA}, \ +} + +#ifdef FLASH_USING_FLASH_INFO_TABLE +/* FLASH supported flash chip information table. If the flash not support JEDEC JESD216 standard, + * then the FLASH will find the flash chip information by this table. You can add other flash to here then + * notice me for update it. The configuration information name and index reference the flash_flash_chip structure. + * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd | + */ +#define FLASH_FLASH_CHIP_TABLE \ +{ \ + {"AT45DB161E", FLASH_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, FLASH_WM_BYTE|FLASH_WM_DUAL_BUFFER, 512, 0x81}, \ + {"W25Q40BV", FLASH_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"W25X40CL", FLASH_MF_ID_WINBOND, 0x30, 0x13, 512L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"W25X16AV", FLASH_MF_ID_WINBOND, 0x30, 0x15, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"W25Q16BV", FLASH_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"W25Q32BV", FLASH_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"W25Q64CV", FLASH_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"W25Q64DW", FLASH_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"W25Q128BV", FLASH_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"W25Q256FV", FLASH_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"SST25VF080B", FLASH_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, FLASH_WM_BYTE|FLASH_WM_AAI, 4096, 0x20}, \ + {"SST25VF016B", FLASH_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, FLASH_WM_BYTE|FLASH_WM_AAI, 4096, 0x20}, \ + {"M25P32", FLASH_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 64L*1024L, 0xD8}, \ + {"M25P80", FLASH_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, FLASH_WM_PAGE_256B, 64L*1024L, 0xD8}, \ + {"M25P40", FLASH_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, FLASH_WM_PAGE_256B, 64L*1024L, 0xD8}, \ + {"EN25Q32B", FLASH_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"GD25Q64B", FLASH_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"GD25Q16B", FLASH_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"GD25Q32C", FLASH_MF_ID_GIGADEVICE, 0x40, 0x16, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"S25FL216K", FLASH_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"S25FL032P", FLASH_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"A25L080", FLASH_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"F25L004", FLASH_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, FLASH_WM_BYTE|FLASH_WM_AAI, 4096, 0x20}, \ + {"PCT25VF016B", FLASH_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, FLASH_WM_BYTE|FLASH_WM_AAI, 4096, 0x20}, \ + {"NM25Q128EVB", FLASH_MF_ID_NOR_MEM, 0x21, 0x18, 16L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"P25D05H", FLASH_MF_ID_PUYA, 0x60, 0x13, 5L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"P25D10H", FLASH_MF_ID_PUYA, 0x60, 0x12, 1L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"P25D20H", FLASH_MF_ID_PUYA, 0x60, 0x11, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"P25D40H", FLASH_MF_ID_PUYA, 0x60, 0x10, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ + {"P25Q80H", FLASH_MF_ID_PUYA, 0x30, 0x14, 8L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20}, \ +} +#endif /* FLASH_USING_FLASH_INFO_TABLE */ + +#ifdef FLASH_USING_QSPI +/* This table saves flash read-fast instructions in QSPI mode, + * FLASH can use this table to select the most appropriate read instruction for flash. + * | mf_id | type_id | capacity_id | qspi_read_mode | + */ +#define FLASH_FLASH_EXT_INFO_TABLE \ +{ \ + /* W25Q40BV */ \ + {FLASH_MF_ID_WINBOND, 0x40, 0x13, NORMAL_SPI_READ|DUAL_OUTPUT}, \ + /* W25Q80JV */ \ + {FLASH_MF_ID_WINBOND, 0x40, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT}, \ + /* W25Q16BV */ \ + {FLASH_MF_ID_WINBOND, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT}, \ + /* W25Q32BV */ \ + {FLASH_MF_ID_WINBOND, 0x40, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_OUTPUT|QUAD_IO}, \ + /* W25Q64JV */ \ + {FLASH_MF_ID_WINBOND, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \ + /* W25Q128JV */ \ + {FLASH_MF_ID_WINBOND, 0x40, 0x18, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \ + /* W25Q256FV */ \ + {FLASH_MF_ID_WINBOND, 0x40, 0x19, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \ + /* EN25Q32B */ \ + {FLASH_MF_ID_EON, 0x30, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_IO}, \ + /* S25FL216K */ \ + {FLASH_MF_ID_CYPRESS, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT}, \ + /* A25L080 */ \ + {FLASH_MF_ID_AMIC, 0x30, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO}, \ + /* A25LQ64 */ \ + {FLASH_MF_ID_AMIC, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_IO}, \ + /* MX25L3206E and KH25L3206E */ \ + {FLASH_MF_ID_MACRONIX, 0x20, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT}, \ + /* MX25L51245G */ \ + {FLASH_MF_ID_MACRONIX, 0x20, 0x1A, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \ + /* GD25Q64B */ \ + {FLASH_MF_ID_GIGADEVICE, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT}, \ + /* NM25Q128EVB */ \ + {FLASH_MF_ID_NOR_MEM, 0x21, 0x18, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \ +} +#endif /* FLASH_USING_QSPI */ + + + + + + +/** + * retry process + * + * @param delay delay function for every retry. NULL will not delay for every retry. + * @param retry retry counts + * @param result FLASH_ERR_TIMEOUT: retry timeout + */ +#define FLASH_RETRY_PROCESS(delay, retry, result) \ + void (*__delay_temp)(void) = (void (*)(void))delay; \ + if (retry == 0) {result = FLASH_ERR_TIMEOUT;break;} \ + else {if (__delay_temp) {__delay_temp();} retry --;} + +/* software version number */ +#define FLASH_SW_VERSION "1.1.0" +/* + * all defined supported command + */ +#ifndef FLASH_CMD_WRITE_ENABLE +#define FLASH_CMD_WRITE_ENABLE 0x06 +#endif + +#ifndef FLASH_CMD_WRITE_DISABLE +#define FLASH_CMD_WRITE_DISABLE 0x04 +#endif + +#ifndef FLASH_CMD_READ_STATUS_REGISTER +#define FLASH_CMD_READ_STATUS_REGISTER 0x05 +#endif + +#ifndef FLASH_VOLATILE_SR_WRITE_ENABLE +#define FLASH_VOLATILE_SR_WRITE_ENABLE 0x50 +#endif + +#ifndef FLASH_CMD_WRITE_STATUS_REGISTER +#define FLASH_CMD_WRITE_STATUS_REGISTER 0x01 +#endif + +#ifndef FLASH_CMD_PAGE_PROGRAM +#define FLASH_CMD_PAGE_PROGRAM 0x02 +#endif + +#ifndef FLASH_CMD_AAI_WORD_PROGRAM +#define FLASH_CMD_AAI_WORD_PROGRAM 0xAD +#endif + +#ifndef FLASH_CMD_ERASE_CHIP +#define FLASH_CMD_ERASE_CHIP 0xC7 +#endif + +#ifndef FLASH_CMD_READ_DATA +#define FLASH_CMD_READ_DATA 0x03 +#endif + +#ifndef FLASH_CMD_FAST_READ_DATA +#define FLASH_CMD_FAST_READ_DATA 0x0B +#endif + +#ifndef FLASH_CMD_DUAL_OUTPUT_READ_DATA +#define FLASH_CMD_DUAL_OUTPUT_READ_DATA 0x3B +#endif + +#ifndef FLASH_CMD_DUAL_IO_READ_DATA +#define FLASH_CMD_DUAL_IO_READ_DATA 0xBB +#endif + +#ifndef FLASH_CMD_QUAD_IO_READ_DATA +#define FLASH_CMD_QUAD_IO_READ_DATA 0xEB +#endif + +#ifndef FLASH_CMD_QUAD_OUTPUT_READ_DATA +#define FLASH_CMD_QUAD_OUTPUT_READ_DATA 0x6B +#endif + +#ifndef FLASH_CMD_MANUFACTURER_DEVICE_ID +#define FLASH_CMD_MANUFACTURER_DEVICE_ID 0x90 +#endif + +#ifndef FLASH_CMD_JEDEC_ID +#define FLASH_CMD_JEDEC_ID 0x9F +#endif + +#ifndef FLASH_CMD_READ_UNIQUE_ID +#define FLASH_CMD_READ_UNIQUE_ID 0x4B +#endif + +#ifndef FLASH_CMD_READ_SFDP_REGISTER +#define FLASH_CMD_READ_SFDP_REGISTER 0x5A +#endif + +#ifndef FLASH_CMD_ENABLE_RESET +#define FLASH_CMD_ENABLE_RESET 0x66 +#endif + +#ifndef FLASH_CMD_RESET +#define FLASH_CMD_RESET 0x99 +#endif + +#ifndef FLASH_CMD_ENTER_4B_ADDRESS_MODE +#define FLASH_CMD_ENTER_4B_ADDRESS_MODE 0xB7 +#endif + +#ifndef FLASH_CMD_EXIT_4B_ADDRESS_MODE +#define FLASH_CMD_EXIT_4B_ADDRESS_MODE 0xE9 +#endif + +#ifndef FLASH_WRITE_MAX_PAGE_SIZE +#define FLASH_WRITE_MAX_PAGE_SIZE 256 +#endif + +/* send dummy data for read data */ +#ifndef FLASH_DUMMY_DATA +#define FLASH_DUMMY_DATA 0xFF +#endif + +/* dummy data count for fast read data and etc */ +#ifndef FLASH_READ_DUMMY_BYTE_CNT +#ifdef FLASH_USING_FAST_READ +#define FLASH_READ_DUMMY_BYTE_CNT 1 +#else +#define FLASH_READ_DUMMY_BYTE_CNT 0 +#endif +#endif + +/* maximum number of erase type support on JESD216 (V1.0) */ +#define FLASH_SFDP_ERASE_TYPE_MAX_NUM 4 + +/** + * status register bits + */ +enum { + FLASH_STATUS_REGISTER_BUSY = (1 << 0), /**< busing */ + FLASH_STATUS_REGISTER_WEL = (1 << 1), /**< write enable latch */ + FLASH_STATUS_REGISTER_SRP = (1 << 7), /**< status register protect */ +}; + +/** + * error code + */ +typedef enum { + FLASH_SUCCESS = 0, /**< success */ + FLASH_ERR_NOT_FOUND = 1, /**< not found or not supported */ + FLASH_ERR_WRITE = 2, /**< write error */ + FLASH_ERR_READ = 3, /**< read error */ + FLASH_ERR_TIMEOUT = 4, /**< timeout error */ + FLASH_ERR_ADDR_OUT_OF_BOUND = 5, /**< address is out of flash bound */ +} flash_err; + +#ifdef FLASH_USING_QSPI +/** + * QSPI flash read cmd format + */ +typedef struct { + uint8_t instruction; + uint8_t instruction_lines; + uint8_t address_size; + uint8_t address_lines; + uint8_t alternate_bytes_lines; + uint8_t dummy_cycles; + uint8_t data_lines; +} flash_qspi_read_cmd_format; +#endif /* FLASH_USING_QSPI */ + +/* SPI bus write read data function type */ +typedef flash_err (*spi_write_read_func)(const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, + size_t read_size); + +#ifdef FLASH_USING_SFDP +/** + * the SFDP (Serial Flash Discoverable Parameters) parameter info which used on this library + */ +typedef struct { + bool available; /**< available when read SFDP OK */ + uint8_t major_rev; /**< SFDP Major Revision */ + uint8_t minor_rev; /**< SFDP Minor Revision */ + uint16_t write_gran; /**< write granularity (bytes) */ + uint8_t erase_4k; /**< 4 kilobyte erase is supported throughout the device */ + uint8_t erase_4k_cmd; /**< 4 Kilobyte erase command */ + bool sr_is_non_vola; /**< status register is supports non-volatile */ + uint8_t vola_sr_we_cmd; /**< volatile status register write enable command */ + bool addr_3_byte; /**< supports 3-Byte addressing */ + bool addr_4_byte; /**< supports 4-Byte addressing */ + uint32_t capacity; /**< flash capacity (bytes) */ + struct { + uint32_t size; /**< erase sector size (bytes). 0x00: not available */ + uint8_t cmd; /**< erase command */ + } eraser[FLASH_SFDP_ERASE_TYPE_MAX_NUM]; /**< supported eraser types table */ + //TODO lots of fast read-related stuff (like modes supported and number of wait states/dummy cycles needed in each) +} flash_sfdp, *flash_sfdp_t; +#endif + +/** + * SPI device + */ +typedef struct __flash_spi { + /* SPI device name */ + char *name; + + /* SPI bus write read data function */ + flash_err (*wr)(const struct __flash_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, + size_t read_size); + +#ifdef FLASH_USING_QSPI + + /* QSPI fast read function */ + flash_err + (*qspi_read)(const struct __flash_spi *spi, uint32_t addr, flash_qspi_read_cmd_format *qspi_read_cmd_format, + uint8_t *read_buf, size_t read_size); + +#endif + + /* lock SPI bus */ + void (*lock)(const struct __flash_spi *spi); + + /* unlock SPI bus */ + void (*unlock)(const struct __flash_spi *spi); + + /* some user data */ + void *user_data; +} flash_spi, *flash_spi_t; + + +#ifdef __cplusplus +} +#endif +#endif //HW_LIB_FLASH_DEF_H diff --git a/lib/oled/inc/oled.h b/lib/oled/inc/oled.h index 8d752f0..6fdb975 100644 --- a/lib/oled/inc/oled.h +++ b/lib/oled/inc/oled.h @@ -95,15 +95,17 @@ struct OLED_Dev * @return void * @example OLED_Init(&oled_dev); */ -void OLED_Init(OLED_T* dev); +void OLED_Init(OLED_T *dev); /** * @brief OLED初始化 * @param dev: [输入] OLED设备指针 +* @param cmd: [输入] OLED初始化指令表 +* @param len: [输入] OLED初始化指令表长度 * @return void * @example OLED_Init(&oled_dev); */ -void OLED_Init_CMD(OLED_T* dev, uint8_t* cmd, uint16_t len); +void OLED_Init_CMD(OLED_T *dev, uint8_t *cmd, uint16_t len); /** * @brief 打开OLED显示 * @param dev: [输入] OLED设备指针 diff --git a/lib/oled/oled.cpp b/lib/oled/oled.cpp index 524948d..6fd8085 100644 --- a/lib/oled/oled.cpp +++ b/lib/oled/oled.cpp @@ -18,8 +18,8 @@ const uint8_t initCmd[] = { 0x40, 0x00, // 设置显示起始行 0x8D, 0x14, // 电荷泵设置 0x20, 0x00, // 设置内存寻址模式 - 0xA0, // 设置段重映射 - 0xC8, // 设置COM输出扫描方向 + 0xA0, // 设置段重映射 从左到右A1 //左右翻转A0 + 0xC8, // 设置COM输出扫描方向 从上到下C8 //上下颠倒C0 0xDA, 0x12, // 设置COM引脚硬件配置 0x81, 0xCF, // 设置对比度控制 0xD9, 0xF1, // 设置预充电周期 diff --git a/lib/tft/inc/ST7735.h b/lib/tft/inc/ST7735.h index 272a952..82cdeac 100644 --- a/lib/tft/inc/ST7735.h +++ b/lib/tft/inc/ST7735.h @@ -118,7 +118,6 @@ extern "C" { /** * @brief LCD_Type_Define */ -#define ST7735_1_8_inch_screen 0x00U #define ST7735_0_9_inch_screen 0x01U #define ST7735_1_8a_inch_screen 0x02U /** @@ -135,9 +134,13 @@ extern "C" { #define ST7735_DELAY 120 -#define ST7735_PANEL HannStar_Panel -#define ST7735_TYPE ST7735_0_9_inch_screen +#define ST7735_PANEL BOE_Panel +#define ST7735_TYPE ST7735_1_8a_inch_screen +//#define ST7735_X_OFFSET 0 +//#define ST7735_Y_OFFSET 0 +//#define ST7735_X_OFFSET 0 +//#define ST7735_Y_OFFSET 0 #if ST7735_TYPE == ST7735_0_9_inch_screen //0.96 ST7735 #if ST7735_PANEL == HannStar_Panel @@ -176,8 +179,11 @@ const uint8_t st7735initcmd[] = { 3, ST7735_PWR_CTRL4, 0x8A, 0x2A, 3, ST7735_PWR_CTRL5, 0x8A, 0xEE, 2, ST7735_VCOMH_VCOML_CTRL1, 0x0E, +#if ST7735_PANEL == HannStar_Panel 1, ST7735_DISPLAY_INVERSION_ON,//HannStar_Panel -// 1,ST7735_DISPLAY_INVERSION_OFF,//BOE_Panel +#else + 1, ST7735_DISPLAY_INVERSION_OFF,//BOE_Panel +#endif 2, ST7735_COLOR_MODE, ST7735_FORMAT_RBG565, 17, ST7735_PV_GAMMA_CTRL, 0x02, 0x1C, 0x07U, 0x12U, 0x37U, 0x32U, 0x29U, 0x2DU, 0x29U, 0x25U, 0x2BU, 0x39U, 0x00U, 0x01U, 0x03U, 0x10U, diff --git a/lib/tft/inc/tft.h b/lib/tft/inc/tft.h index c8d36d7..5dfff39 100644 --- a/lib/tft/inc/tft.h +++ b/lib/tft/inc/tft.h @@ -283,6 +283,8 @@ void TFT_DrawRect(TFT_T *dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2 **/ void TFT_DrawCircle(TFT_T *dev, uint16_t x, uint16_t y, uint16_t r); +void TFT_DrawArc(TFT_T *dev, uint16_t x0, uint16_t y0, uint16_t radius, int start_angle, int end_angle); + /** * @brief 在TFT显示屏上显示单个字符 * @param dev: [输入] TFT设备结构体指针 @@ -359,6 +361,17 @@ void TFT_ShowPic(TFT_T *dev, uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, T **/ void TFT_DrawCross(TFT_T *dev, uint16_t x, uint16_t y, uint8_t r); +/** + * @brief 在屏幕上绘制一个X字交叉线 + * @param dev: [输入] TFT设备指针 + * @param x: [输入] 十字交叉线中心点x坐标 + * @param y: [输入] 十字交叉线中心点y坐标 + * @param r: [输入] 十字交叉线长度的一半(即从中心点到横竖线的长度) + * @return void + * @example TFT_DrawCross(&tft_dev, 100, 80, 5); +**/ +void TFT_DrawXCross(TFT_T *dev, uint16_t x, uint16_t y, uint8_t r); + /** * @brief 在屏幕上显示进度条 * @param dev: [输入] TFT设备指针 @@ -372,6 +385,17 @@ void TFT_DrawCross(TFT_T *dev, uint16_t x, uint16_t y, uint8_t r); **/ void TFT_ShowBar(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height, float progress); +/** + * @brief 绘制带圆角的矩形 + * @param dev: [输入] TFT设备指针 + * @param x: [输入] 矩形左上角x坐标 + * @param y: [输入] 矩形左上角y坐标 + * @param width: [输入] 矩形宽度 + * @param height: [输入] 矩形高度 + * @param radius: [输入] 圆角半径 + * @return void +**/ +void TFT_DrawRoundedRect(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t radius); #ifdef LVGL_FONT @@ -392,6 +416,19 @@ void TFT_DisplayString(TFT_T *dev, const lv_font_t *font, uint8_t *s, uint16_t x #endif +#define ARGB_TO_RGB565(...) \ + _ARGB_TO_RGB565_NARG(__VA_ARGS__, _ARGB_TO_RGB565_4, _ARGB_TO_RGB565_1)(__VA_ARGS__) + +#define _ARGB_TO_RGB565_NARG(...) _ARGB_TO_RGB565_NARG_(__VA_ARGS__, _ARGB_TO_RGB565_RSEQ_N()) +#define _ARGB_TO_RGB565_NARG_(...) _ARGB_TO_RGB565_ARG_N(__VA_ARGS__) + +#define _ARGB_TO_RGB565_ARG_N(_1, _2, _3, _4, N, ...) N +#define _ARGB_TO_RGB565_RSEQ_N() 4, 3, 2, 1, 0 + +#define _ARGB_TO_RGB565_1(argb) (((((argb) >> 8) & 0xF800) | (((argb) >> 5) & 0x07E0) | ((argb) >> 3) & 0x001F) + +#define _ARGB_TO_RGB565_4(a, r, g, b) (((((a) & 0xFF) >> 3) << 11) | ((((r) & 0xFF) >> 2) << 5) | (((g) & 0xFF) >> 3)) + #ifdef __cplusplus } #endif diff --git a/lib/tft/tft.cpp b/lib/tft/tft.cpp index 05f92cf..c33521c 100644 --- a/lib/tft/tft.cpp +++ b/lib/tft/tft.cpp @@ -1,3 +1,4 @@ +#include #include "tft.h" #include "ascii_font.h" @@ -201,11 +202,11 @@ void TFT_FillColor(TFT_T *dev,TFT_Color_t color){ dev->sendData(color.u8, 2); } - +#define swap_(a, b) (a=(a)+(b),b=(a)-(b),a=(a)-(b)) void TFT_DrawLine(TFT_T *dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { - uint16_t i, k, k1, k2; - if ((x1 < 0) || (x2 > dev->width) || (y1 < 0) || (y2 > dev->height) || (x1 > x2) || (y1 > y2))return; + uint16_t i; + if ((x1 < 0) || (x2 > dev->width) || (y1 < 0) || (y2 > dev->height))return; if (x1 == x2) //画竖线 { for (i = 0; i < (y2 - y1); i++) { @@ -217,12 +218,50 @@ void TFT_DrawLine(TFT_T *dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2 TFT_SetPixel(dev, x1 + i, y1,POINT_COLOR); } } else //画斜线 - { - k1 = y2 - y1; - k2 = x2 - x1; - k = k1 * 10 / k2; - for (i = 0; i < (x2 - x1); i++) { - TFT_SetPixel(dev, x1 + i, y1 + i * k / 10,POINT_COLOR); + {//Bresenham算法 + int p, twoDy, twoDyMinusDx, s1, s2; + int dx = abs(x2 - x1), dy = abs(y2 - y1); + if (dy > dx) //斜率大于1 + { + p = 2 * dx - dy; + twoDy = 2 * dx; + twoDyMinusDx = 2 * (dx - dy); + if (y1 > y2)//斜率为负时 反转斜率 + { + swap_(x1, x2); + swap_(y1, y2); + } + s1 = x2 > x1 ? 1 : -1; + TFT_SetPixel(dev, x1, y1, POINT_COLOR); + while (y1 < y2) { + y1++; + if (p < 0) { p += twoDy; } + else { + x1 += s1; + p += twoDyMinusDx; + } + TFT_SetPixel(dev, x1, y1, POINT_COLOR); + } + } else { + p = 2 * dy - dx; + twoDy = 2 * dy; + twoDyMinusDx = 2 * (dy - dx); + if (x1 > x2)//斜率为负时 反转斜率 + { + swap_(x1, x2); + swap_(y1, y2); + } + s2 = y2 > y1 ? 1 : -1; + TFT_SetPixel(dev, x1, y1, POINT_COLOR); + while (x1 < x2) { + x1++; + if (p < 0) { p += twoDy; } + else { + y1 += s2; + p += twoDyMinusDx; + } + TFT_SetPixel(dev, x1, y1, POINT_COLOR); + } } } } @@ -234,29 +273,98 @@ void TFT_DrawRect(TFT_T *dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2 TFT_DrawLine(dev, x1, y2, x2, y2); // Bottom side } -void TFT_DrawCircle(TFT_T *dev, uint16_t x, uint16_t y, uint16_t r) { - int a, b, num; - a = 0; - b = r; - while (2 * b * b >= r * r) { - TFT_SetPixel(dev, x + a, y - b,POINT_COLOR); - TFT_SetPixel(dev, x - a, y - b,POINT_COLOR); - TFT_SetPixel(dev, x - a, y + b,POINT_COLOR); - TFT_SetPixel(dev, x + a, y + b,POINT_COLOR); - TFT_SetPixel(dev, x + b, y + a,POINT_COLOR); - TFT_SetPixel(dev, x + b, y - a,POINT_COLOR); - TFT_SetPixel(dev, x - b, y - a,POINT_COLOR); - TFT_SetPixel(dev, x - b, y + a,POINT_COLOR); +void TFT_DrawCircle(TFT_T *dev, uint16_t x0, uint16_t y0, uint16_t radius) { + int x = radius; + int y = 0; + int decision = 1 - x; - a++; - num = (a * a + b * b) - r * r;//计算画的点离圆心的距离 - if (num > 0) { - b--; - a--; + while (y <= x) { + TFT_SetPixel(dev, x0 + x, y0 + y, POINT_COLOR); + TFT_SetPixel(dev, x0 + y, y0 + x, POINT_COLOR); + TFT_SetPixel(dev, x0 - y, y0 + x, POINT_COLOR); + TFT_SetPixel(dev, x0 - x, y0 + y, POINT_COLOR); + TFT_SetPixel(dev, x0 - x, y0 - y, POINT_COLOR); + TFT_SetPixel(dev, x0 - y, y0 - x, POINT_COLOR); + TFT_SetPixel(dev, x0 + y, y0 - x, POINT_COLOR); + TFT_SetPixel(dev, x0 + x, y0 - y, POINT_COLOR); + + y++; + + if (decision <= 0) { + decision += 2 * y + 1; + } else { + x--; + decision += 2 * (y - x) + 1; } } } +#define PI 3.14159265359 + +#define PI 3.14159265359 + +//void TFT_DrawArc(TFT_T *dev, uint16_t x0, uint16_t y0, uint16_t radius, int start_angle, int end_angle) { +// int x = radius; +// int y = 0; +// int decision = 1 - x; +// int change = 4 * (1 - x); +// +// int angle = start_angle; +// +// while (y <= x) { +// if (angle >= start_angle && angle <= end_angle) { +// TFT_SetPixel(dev, x0 + x, y0 + y, POINT_COLOR); +// TFT_SetPixel(dev, x0 + y, y0 + x, POINT_COLOR); +// TFT_SetPixel(dev, x0 - y, y0 + x, POINT_COLOR); +// TFT_SetPixel(dev, x0 - x, y0 + y, POINT_COLOR); +// TFT_SetPixel(dev, x0 - x, y0 - y, POINT_COLOR); +// TFT_SetPixel(dev, x0 - y, y0 - x, POINT_COLOR); +// TFT_SetPixel(dev, x0 + y, y0 - x, POINT_COLOR); +// TFT_SetPixel(dev, x0 + x, y0 - y, POINT_COLOR); +// } +// +// if (decision <= 0) { +// decision += 2 * y + 1; +// change += 2; +// } else { +// x--; +// decision += 2 * (y - x) + 1; +// change += 4 * ((y - x) + 1); +// } +// +// y++; +// angle = (int)(0.5 + (atan((double)y / x) * 180.0 / PI)); +// } +//} + +#define PI 3.14159265359 + +void TFT_DrawArc(TFT_T *dev, uint16_t x, uint16_t y, uint16_t radius, int start_angle, int end_angle) { + // 修复输入角度范围 + if (start_angle < 0) { + start_angle = 0; + } else if (start_angle > 360) { + start_angle = 360; + } + if (end_angle < 0) { + end_angle = 0; + } else if (end_angle > 360) { + end_angle = 360; + } + double angle; + unsigned int cirx, ciry, lastX, lastY; + cirx = x; + ciry = y; + + angle = start_angle; + while (angle <= end_angle) { + lastX = cirx + radius * sin(angle * PI / 180); + lastY = ciry - radius * cos(angle * PI / 180); + TFT_SetPixel(dev, lastX, lastY, POINT_COLOR); + angle = angle + 0.1; + } +} + void TFT_ShowChar(TFT_T *dev, uint16_t x, uint16_t y, uint8_t chr, uint16_t size,bool mode) { uint16_t i, m, temp, size2, chr1; uint16_t ys = y; @@ -446,6 +554,26 @@ void TFT_DrawCross(TFT_T *dev, uint16_t x, uint16_t y, uint8_t r) { TFT_DrawLine(dev, x, y - r, x, y + r); } +void TFT_DrawXCross(TFT_T *dev, uint16_t x, uint16_t y, uint8_t r) { + TFT_DrawLine(dev, x - r, y - r, x + r, y + r); + TFT_DrawLine(dev, x - r, y + r, x + r, y - r); +} + +void TFT_DrawRoundedRect(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t radius) { + // 画四条直线 + TFT_DrawLine(dev, x + radius, y, x + width - radius, y); // 上方横线 + TFT_DrawLine(dev, x + radius, y + height, x + width - radius, y + height); // 下方横线 + + TFT_DrawLine(dev, x, y + radius, x, y + height - radius); // 左侧竖线 + TFT_DrawLine(dev, x + width, y + radius, x + width, y + height - radius); // 右侧竖线 + + // 画四个圆角 + TFT_DrawArc(dev, x + radius, y + radius, radius, 270, 360); // 左上角 + TFT_DrawArc(dev, x + width - radius, y + radius, radius, 0, 90); // 右上角 + TFT_DrawArc(dev, x + width - radius, y + height - radius, radius, 90, 180); // 右下角 + TFT_DrawArc(dev, x + radius, y + height - radius, radius, 180, 270); // 左下角 +} + uint16_t invertRGB565(uint16_t color) { // 分离颜色分量 uint8_t r = (color >> 11) & 0x1F; // 取出红色分量 diff --git a/lib/utils/inc/log.h b/lib/utils/inc/log.h index 4b1d28a..fbd85dd 100644 --- a/lib/utils/inc/log.h +++ b/lib/utils/inc/log.h @@ -42,11 +42,11 @@ //////////////////////// #ifdef LOG_WITH_RUN_TIMER -#define LOG_RUN_TIMER_FUN GetSysCnt32() +#define LOG_RUN_TIMER_FUN GetTickCount64() #define LOG_RUN_TIMER_FMT "-T(%lums)" #else -#define LOG_RUN_TIMER_FUN 0 -#define LOG_RUN_TIMER_FMT "-T(%lu)" +#define LOG_RUN_TIMER_FUN GetTickCount64() +#define LOG_RUN_TIMER_FMT "(%llums)" #endif #ifdef LOG_LINE_END_CRLF @@ -65,6 +65,9 @@ #endif #endif +#define LOG_OUT_FUNC printf + + #define LOG_BASE_FILENAME \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : \ strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) @@ -101,9 +104,9 @@ // 带时钟输出 #if defined(LOG_TIMER) #ifdef LOG_WITH_RUN_TIMER -#define LOGT(tag, fmt, ...) do{ printf(LOG_COLOR_BLUE "[" tag "]" LOG_RUN_TIMER_FMT ": " fmt LOG_END,LOG_RUN_TIMER_FUN, ##__VA_ARGS__); } while(0) +#define LOGT(tag, fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_BLUE "[" tag "]" LOG_RUN_TIMER_FMT ": " fmt LOG_END,LOG_RUN_TIMER_FUN, ##__VA_ARGS__); } while(0) #else -#define LOGT(tag, fmt, ...) do{ printf(LOG_COLOR_BLUE "[" tag "]: " fmt LOG_END, ##__VA_ARGS__); } while(0) +#define LOGT(tag, fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_BLUE "[" tag "]: " fmt LOG_END, ##__VA_ARGS__); } while(0) #endif #else #define LOGT(fmt, ...) ((void)0) @@ -112,9 +115,9 @@ // 等级输出 #if LOG_OUTPUT_LVL >= LOG_LVL_FATAL #ifdef LOG_WITH_RUN_TIMER -#define LOGF(fmt, ...) do{ printf(LOG_COLOR_CYAN "[F]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); LOG_EXIT_PROGRAM(); } while(0) +#define LOGF(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_CYAN "[F]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); LOG_EXIT_PROGRAM(); } while(0) #else -#define LOGF(fmt, ...) do{ printf(LOG_COLOR_CYAN "[F]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); LOG_EXIT_PROGRAM(); } while(0) +#define LOGF(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_CYAN "[F]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); LOG_EXIT_PROGRAM(); } while(0) #endif #else #define LOGF(fmt, ...) ((void)0) @@ -122,9 +125,9 @@ #if LOG_OUTPUT_LVL >= LOG_LVL_ERROR #ifdef LOG_WITH_RUN_TIMER -#define LOGE(fmt, ...) do{ printf(LOG_COLOR_RED "[E]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0) +#define LOGE(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_RED "[E]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0) #else -#define LOGE(fmt, ...) do{ printf(LOG_COLOR_RED "[E]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0) +#define LOGE(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_RED "[E]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0) #endif #else #define LOGE(fmt, ...) ((void)0) @@ -132,9 +135,9 @@ #if LOG_OUTPUT_LVL >= LOG_LVL_WARN #ifdef LOG_WITH_RUN_TIMER -#define LOGW(fmt, ...) do{ printf(LOG_COLOR_CARMINE "[W]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0) +#define LOGW(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_CARMINE "[W]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0) #else -#define LOGW(fmt, ...) do{ printf(LOG_COLOR_CARMINE "[W]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0) +#define LOGW(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_CARMINE "[W]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0) #endif #else #define LOGW(fmt, ...) ((void)0) @@ -142,9 +145,9 @@ #if LOG_OUTPUT_LVL >= LOG_LVL_INFO #ifdef LOG_WITH_RUN_TIMER -#define LOGI(fmt, ...) do{ printf(LOG_COLOR_YELLOW "[I]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) +#define LOGI(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_YELLOW "[I]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) #else -#define LOGI(fmt, ...) do{ printf(LOG_COLOR_YELLOW "[I]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) +#define LOGI(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_YELLOW "[I]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) #endif #else #define LOGI(fmt, ...) ((void)0) @@ -152,9 +155,9 @@ #if LOG_OUTPUT_LVL >= LOG_LVL_DEBUG #ifdef LOG_WITH_RUN_TIMER -#define LOGD(fmt, ...) do{ printf(LOG_COLOR_DEFAULT "[D]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) +#define LOGD(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_DEFAULT "[D]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) #else -#define LOGD(fmt, ...) do{ printf(LOG_COLOR_DEFAULT "[D]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) +#define LOGD(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_DEFAULT "[D]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) #endif #else #define LOGD(fmt, ...) ((void)0) @@ -162,11 +165,164 @@ #if LOG_OUTPUT_LVL >= LOG_LVL_VERBOSE #ifdef LOG_WITH_RUN_TIMER -#define LOGV(fmt, ...) do{ printf(LOG_COLOR_DEFAULT "[V]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) +#define LOGV(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_DEFAULT "[V]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) #else -#define LOGV(fmt, ...) do{ printf(LOG_COLOR_DEFAULT "[V]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) +#define LOGV(fmt, ...) do{ LOG_OUT_FUNC(LOG_COLOR_DEFAULT "[V]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0) #endif #else #define LOGV(fmt, ...) ((void)0) #endif -#endif //HW_LIB_LOG_H \ No newline at end of file + + +#ifndef LOG_RUN_TIME +#ifndef __PLOOC_VA_NUM_ARGS_IMPL +# define __PLOOC_VA_NUM_ARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, \ + _12, _13, _14, _15, _16, __N, ...) __N +#endif + +#ifndef __PLOOC_VA_NUM_ARGS +#define __PLOOC_VA_NUM_ARGS(...) \ + __PLOOC_VA_NUM_ARGS_IMPL( 0,##__VA_ARGS__,16,15,14,13,12,11,10,9, \ + 8,7,6,5,4,3,2,1,0) +#endif + +#ifndef __ticks_sync_barrier__ + +/* default implementation */ +#if defined(__clang__) || __IS_COMPILER_GCC__ +# define __ticks_sync_barrier__(...) __sync_synchronize() +#else +# define __ticks_sync_barrier__(...) +#endif + +#endif + +#undef __CONNECT2 +#undef __CONNECT3 +#undef __CONNECT4 +#undef __CONNECT5 +#undef __CONNECT6 +#undef __CONNECT7 +#undef __CONNECT8 +#undef __CONNECT9 + +#undef CONNECT2 +#undef CONNECT3 +#undef CONNECT4 +#undef CONNECT5 +#undef CONNECT6 +#undef CONNECT7 +#undef CONNECT8 +#undef CONNECT9 + +#undef CONNECT + +#undef __MACRO_EXPANDING +#define __MACRO_EXPANDING(...) __VA_ARGS__ + +#define __CONNECT2(__A, __B) __A##__B +#define __CONNECT3(__A, __B, __C) __A##__B##__C +#define __CONNECT4(__A, __B, __C, __D) __A##__B##__C##__D +#define __CONNECT5(__A, __B, __C, __D, __E) __A##__B##__C##__D##__E +#define __CONNECT6(__A, __B, __C, __D, __E, __F) __A##__B##__C##__D##__E##__F +#define __CONNECT7(__A, __B, __C, __D, __E, __F, __G) \ + __A##__B##__C##__D##__E##__F##__G +#define __CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H) \ + __A##__B##__C##__D##__E##__F##__G##__H +#define __CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I) \ + __A##__B##__C##__D##__E##__F##__G##__H##__I + +#define ALT_CONNECT2(__A, __B) __CONNECT2(__A, __B) +#define CONNECT2(__A, __B) __CONNECT2(__A, __B) +#define CONNECT3(__A, __B, __C) __CONNECT3(__A, __B, __C) +#define CONNECT4(__A, __B, __C, __D) __CONNECT4(__A, __B, __C, __D) +#define CONNECT5(__A, __B, __C, __D, __E) __CONNECT5(__A, __B, __C, __D, __E) +#define CONNECT6(__A, __B, __C, __D, __E, __F) \ + __CONNECT6(__A, __B, __C, __D, __E, __F) +#define CONNECT7(__A, __B, __C, __D, __E, __F, __G) \ + __CONNECT7(__A, __B, __C, __D, __E, __F, __G) +#define CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H) \ + __CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H) +#define CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I) \ + __CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I) + +#define CONNECT(...) \ + ALT_CONNECT2(CONNECT, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) + +#undef __using1 +#undef __using2 +#undef __using3 +#undef __using4 +#undef using + +#define __using1(__declare) \ + for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \ + CONNECT3(__using_, __LINE__,_ptr)++ == NULL; \ + ) + +#define __using2(__declare, __on_leave_expr) \ + for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \ + CONNECT3(__using_, __LINE__,_ptr)++ == NULL; \ + (__on_leave_expr) \ + ) + +#define __using3(__declare, __on_enter_expr, __on_leave_expr) \ + for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \ + CONNECT3(__using_, __LINE__,_ptr)++ == NULL ? \ + ((__on_enter_expr),1) : 0; \ + (__on_leave_expr) \ + ) + +#define __using4(__dcl1, __dcl2, __on_enter_expr, __on_leave_expr) \ + for (__dcl1, __dcl2, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \ + CONNECT3(__using_, __LINE__,_ptr)++ == NULL ? \ + ((__on_enter_expr),1) : 0; \ + (__on_leave_expr) \ + ) + +#define using(...) \ + CONNECT2(__using, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) + +#ifdef LOG_WITH_RUN_TIMER +#define LOG_RUN_TIME(__STR, ...) \ + using(uint64_t _ = LOG_RUN_TIMER_FUN, __time_count__ = _, \ + {__ticks_sync_barrier__();}, \ + { \ + __ticks_sync_barrier__(); \ + _ = LOG_RUN_TIMER_FUN - _; \ + __time_count__ = _; \ + if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) { \ + LOG_OUT_FUNC("\r\n"); \ + LOG_OUT_FUNC("-[Run Timer]"); \ + LOG_OUT_FUNC( \ + "------------------------------------\r\n"); \ + LOG_OUT_FUNC( \ + __STR " total run timer: " LOG_RUN_TIMER_FMT LOG_END, \ + _); \ + } else { \ + __VA_ARGS__ \ + }; \ + }) +#else +#define LOG_RUN_TIME(__STR, ...) \ + using(uint64_t _ = LOG_RUN_TIMER_FUN, __time_count__ = _, \ + {__ticks_sync_barrier__();}, \ + { \ + __ticks_sync_barrier__(); \ + _ = LOG_RUN_TIMER_FUN - _; \ + __time_count__ = _; \ + if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) { \ + LOG_OUT_FUNC("\r\n"); \ + LOG_OUT_FUNC("-[Run Timer]"); \ + LOG_OUT_FUNC( \ + "------------------------------------\r\n"); \ + LOG_OUT_FUNC( \ + __STR " total run timer: " LOG_RUN_TIMER_FMT LOG_END, \ + _); \ + } else { \ + __VA_ARGS__ \ + }; \ + }) +#endif +#endif +#endif //HW_LIB_LOG_H diff --git a/lib/utils/inc/ticks.h b/lib/utils/inc/ticks.h new file mode 100644 index 0000000..9deacda --- /dev/null +++ b/lib/utils/inc/ticks.h @@ -0,0 +1,806 @@ +// +// Created by lydxh on 24-11-27. +// + +#ifndef HW_LIB_TICKS_H +#define HW_LIB_TICKS_H +/*============================ INCLUDES ======================================*/ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +/*============================ MACROS ========================================*/ + + +/*! + * \addtogroup gHelper 4 Helper + * @{ + */ + +// for IAR +#undef __IS_COMPILER_IAR__ +#if defined(__IAR_SYSTEMS_ICC__) +# define __IS_COMPILER_IAR__ 1 +#endif + +// for arm compiler 5 +#undef __IS_COMPILER_ARM_COMPILER_5__ +#if ((__ARMCC_VERSION >= 5000000) && (__ARMCC_VERSION < 6000000)) +# define __IS_COMPILER_ARM_COMPILER_5__ 1 +#endif + + +//for arm compiler 6 + +#undef __IS_COMPILER_ARM_COMPILER_6__ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +# define __IS_COMPILER_ARM_COMPILER_6__ 1 +#endif +#undef __IS_COMPILER_ARM_COMPILER__ +#if defined(__IS_COMPILER_ARM_COMPILER_5__) && __IS_COMPILER_ARM_COMPILER_5__ \ + || defined(__IS_COMPILER_ARM_COMPILER_6__) && __IS_COMPILER_ARM_COMPILER_6__ +# define __IS_COMPILER_ARM_COMPILER__ 1 +#endif + +// for clang +#undef __IS_COMPILER_LLVM__ +#if defined(__clang__) && !__IS_COMPILER_ARM_COMPILER_6__ +# define __IS_COMPILER_LLVM__ 1 +#else + +// for gcc +# undef __IS_COMPILER_GCC__ +# if defined(__GNUC__) && !(defined(__IS_COMPILER_ARM_COMPILER__) \ + || defined(__IS_COMPILER_LLVM__) \ + || defined(__IS_COMPILER_IAR__)) +# define __IS_COMPILER_GCC__ 1 +# endif + +#endif + + +#ifdef __PERF_COUNT_PLATFORM_SPECIFIC_HEADER__ +# include __PERF_COUNT_PLATFORM_SPECIFIC_HEADER__ +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-warning-option" +# pragma clang diagnostic ignored "-Wreserved-identifier" +# pragma clang diagnostic ignored "-Wdeclaration-after-statement" +# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +# pragma clang diagnostic ignored "-Wgnu-statement-expression" +# pragma clang diagnostic ignored "-Wunused-but-set-variable" +# pragma clang diagnostic ignored "-Wshadow" +# pragma clang diagnostic ignored "-Wshorten-64-to-32" +# pragma clang diagnostic ignored "-Wcompound-token-split-by-macro" +# pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#elif defined(__IS_COMPILER_ARM_COMPILER_5__) +# pragma diag_suppress 550 +#elif defined(__IS_COMPILER_GCC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wformat=" +#endif + +#ifndef __PLOOC_VA_NUM_ARGS_IMPL +# define __PLOOC_VA_NUM_ARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, \ + _12, _13, _14, _15, _16, __N, ...) __N +#endif + +#ifndef __PLOOC_VA_NUM_ARGS +#define __PLOOC_VA_NUM_ARGS(...) \ + __PLOOC_VA_NUM_ARGS_IMPL( 0,##__VA_ARGS__,16,15,14,13,12,11,10,9, \ + 8,7,6,5,4,3,2,1,0) +#endif + +#ifndef UNUSED_PARAM +# define UNUSED_PARAM(__VAR) (void)(__VAR) +#endif + +#ifndef MIN +# define MIN(__a, __b) ((__a) <= (__b) ? (__a) : (__b)) +#endif + +#ifndef MAX +# define MAX(__a, __b) ((__a) >= (__b) ? (__a) : (__b)) +#endif + +/*! + * \brief an attribute for static variables that no initialisation is required + * in the C startup process. + */ +#ifndef PERF_NOINIT +# if defined(__IS_COMPILER_ARM_COMPILER_5__) +# define PERF_NOINIT __attribute__(( section( ".bss.noinit"),zero_init)) +# elif defined(__IS_COMPILER_ARM_COMPILER_6__) +# define PERF_NOINIT __attribute__(( section( ".bss.noinit"))) +# elif defined(__IS_COMPILER_IAR__) +# define PERF_NOINIT __no_init +# elif (defined(__IS_COMPILER_GCC__) || defined(__IS_COMPILER_LLVM__)) && !defined(__APPLE__) +# define PERF_NOINIT __attribute__(( section( ".bss.noinit"))) +# else +# define PERF_NOINIT +# endif +#endif + + +#undef __CONNECT2 +#undef __CONNECT3 +#undef __CONNECT4 +#undef __CONNECT5 +#undef __CONNECT6 +#undef __CONNECT7 +#undef __CONNECT8 +#undef __CONNECT9 + +#undef CONNECT2 +#undef CONNECT3 +#undef CONNECT4 +#undef CONNECT5 +#undef CONNECT6 +#undef CONNECT7 +#undef CONNECT8 +#undef CONNECT9 + +#undef CONNECT + +#undef __MACRO_EXPANDING +#define __MACRO_EXPANDING(...) __VA_ARGS__ + +#define __CONNECT2(__A, __B) __A##__B +#define __CONNECT3(__A, __B, __C) __A##__B##__C +#define __CONNECT4(__A, __B, __C, __D) __A##__B##__C##__D +#define __CONNECT5(__A, __B, __C, __D, __E) __A##__B##__C##__D##__E +#define __CONNECT6(__A, __B, __C, __D, __E, __F) __A##__B##__C##__D##__E##__F +#define __CONNECT7(__A, __B, __C, __D, __E, __F, __G) \ + __A##__B##__C##__D##__E##__F##__G +#define __CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H) \ + __A##__B##__C##__D##__E##__F##__G##__H +#define __CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I) \ + __A##__B##__C##__D##__E##__F##__G##__H##__I + +#define ALT_CONNECT2(__A, __B) __CONNECT2(__A, __B) +#define CONNECT2(__A, __B) __CONNECT2(__A, __B) +#define CONNECT3(__A, __B, __C) __CONNECT3(__A, __B, __C) +#define CONNECT4(__A, __B, __C, __D) __CONNECT4(__A, __B, __C, __D) +#define CONNECT5(__A, __B, __C, __D, __E) __CONNECT5(__A, __B, __C, __D, __E) +#define CONNECT6(__A, __B, __C, __D, __E, __F) \ + __CONNECT6(__A, __B, __C, __D, __E, __F) +#define CONNECT7(__A, __B, __C, __D, __E, __F, __G) \ + __CONNECT7(__A, __B, __C, __D, __E, __F, __G) +#define CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H) \ + __CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H) +#define CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I) \ + __CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I) + +#define CONNECT(...) \ + ALT_CONNECT2(CONNECT, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) + +#undef __using1 +#undef __using2 +#undef __using3 +#undef __using4 +#undef using + +#define __using1(__declare) \ + for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \ + CONNECT3(__using_, __LINE__,_ptr)++ == NULL; \ + ) + +#define __using2(__declare, __on_leave_expr) \ + for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \ + CONNECT3(__using_, __LINE__,_ptr)++ == NULL; \ + (__on_leave_expr) \ + ) + +#define __using3(__declare, __on_enter_expr, __on_leave_expr) \ + for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \ + CONNECT3(__using_, __LINE__,_ptr)++ == NULL ? \ + ((__on_enter_expr),1) : 0; \ + (__on_leave_expr) \ + ) + +#define __using4(__dcl1, __dcl2, __on_enter_expr, __on_leave_expr) \ + for (__dcl1, __dcl2, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \ + CONNECT3(__using_, __LINE__,_ptr)++ == NULL ? \ + ((__on_enter_expr),1) : 0; \ + (__on_leave_expr) \ + ) + +#define using(...) \ + CONNECT2(__using, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) + + +#undef __with2 +#undef __with3 +#undef with + +#define __with1(__addr) \ + using(__typeof__(*__addr) *_=(__addr)) + +#define __with2(__type, __addr) \ + using(__type *_=(__addr)) +#define __with3(__type, __addr, __item) \ + using(__type *_=(__addr), *__item = _, _=_,_=_ ) + +#define with(...) \ + CONNECT2(__with, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) + +#undef _ + +#ifndef dimof +# define dimof(__array) (sizeof(__array)/sizeof(__array[0])) +#endif + + +#define SAFE_NAME(__NAME) CONNECT3(__,__NAME,__LINE__) + +#undef foreach2 +#undef foreach3 +#undef foreach + +#define foreach1(__array) \ + using(__typeof__(__array[0]) *_ = __array) \ + for ( uint_fast32_t SAFE_NAME(count) = dimof(__array); \ + SAFE_NAME(count) > 0; \ + _++, SAFE_NAME(count)-- \ + ) + +#define foreach2(__type, __array) \ + using(__type *_ = __array) \ + for ( uint_fast32_t SAFE_NAME(count) = dimof(__array); \ + SAFE_NAME(count) > 0; \ + _++, SAFE_NAME(count)-- \ + ) + +#define foreach3(__type, __array, __item) \ + using(__type *_ = __array, *__item = _, _ = _, _ = _ ) \ + for ( uint_fast32_t SAFE_NAME(count) = dimof(__array); \ + SAFE_NAME(count) > 0; \ + _++, __item = _, SAFE_NAME(count)-- \ + ) + +#define foreach(...) \ + CONNECT2(foreach, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) + +#ifndef safe_atom_code +# define safe_atom_code() \ + using( ticks_global_interrupt_status_t SAFE_NAME(temp) = \ + ticks_port_disable_global_interrupt(), \ + ticks_port_resume_global_interrupt(SAFE_NAME(temp))) +#endif + + +#ifndef __WEAK +# define __WEAK __attribute__((weak)) +#endif + +#ifndef __STATIC_INLINE +# define __STATIC_INLINE static inline +#endif + +#ifndef __IRQ_SAFE +# define __IRQ_SAFE \ + using( ticks_global_interrupt_status_t SAFE_NAME(temp) = \ + ticks_port_disable_global_interrupt(), \ + ticks_port_resume_global_interrupt(SAFE_NAME(temp))) +#endif + +#ifndef __perf_counter_printf__ +# define __perf_counter_printf__ printf +#endif + +/* deprecated macro for backward compatibility */ +#define user_code_insert_to_systick_handler \ + ticks_port_insert_to_system_timer_insert_ovf_handler + +#if __PLOOC_VA_NUM_ARGS() != 0 +#warning Please enable GNU extensions, it is required by __cycleof__() and \ +__super_loop_monitor__() +#endif + +#if defined(__PERF_COUNTER_CFG_USE_SYSTICK_WRAPPER__) \ + && (!defined(__TICKS_USE_PORTING__) \ + || (defined(__TICKS_USE_PORTING__) && (0 == __TICKS_USE_PORTING__))) + +# if defined(__IS_COMPILER_ARM_COMPILER_5__) && __IS_COMPILER_ARM_COMPILER_5__ +# pragma import(__ensure_systick_wrapper) +# elif (defined(__GNUC__) || defined(__clang__)) \ + && (!defined(__IS_COMPILER_IAR__) || !__IS_COMPILER_IAR__) +__asm(".global __ensure_systick_wrapper\n\t"); +# endif + +#endif + +#ifndef __ticks_sync_barrier__ + +/* default implementation */ +#if defined(__clang__) || __IS_COMPILER_GCC__ +# define __ticks_sync_barrier__(...) __sync_synchronize() +#else +# define __ticks_sync_barrier__(...) +#endif + +#endif +/*! @} */ + +/*============================ MACROFIED FUNCTIONS ===========================*/ + +/*! + * \addtogroup gBasic 1 Basic + * @{ + */ + +/*! + * \brief measure the cycle count of a given code segment + * \param[in] __STR a description string for the measurement + * \param[in] ... an optional code segement, in which we can read the measured + * result from __cycle_count__. + * \details Here is an example: + E.g. + \code + __cycleof__("printf") { + printf("hello world\r\n"); + } + \endcode + */ +#define __cycleof__(__STR, ...) \ + using(int64_t _ = get_system_ticks(), __cycle_count__ = _, \ + {__ticks_sync_barrier__();}, \ + { \ + __ticks_sync_barrier__(); \ + _ = get_system_ticks() - _ - g_nOffset; \ + __cycle_count__ = _; \ + if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) { \ + __perf_counter_printf__("\r\n"); \ + __perf_counter_printf__("-[Cycle Report]"); \ + __perf_counter_printf__( \ + "------------------------------------\r\n"); \ + __perf_counter_printf__( \ + __STR " total cycle count: %ld [%08lx]\r\n", \ + (long)_, (long)_); \ + } else { \ + __VA_ARGS__ \ + }; \ + }) + +/*! + * \brief measure the cpu usage for a given code segment and print out the + * result in percentage. + * \param[in] __CNT generate result on every given iterations + * \param[in] ... an optional code segement, in which we can read the measured + * result from __usage__ which is a float value. + * \details Here is an example, 50% cpu time: + E.g. + \code + while (1) { + __cpu_usage__(100) { + delay_us(5000); + } + delay_us(5000); + } + \endcode + */ +#define __cpu_usage__(__CNT, ...) \ + static int64_t SAFE_NAME(s_lTimestamp) = 0, SAFE_NAME(s_lTotal) = 0; \ + static uint32_t SAFE_NAME(s_wLoopCounter) = (__CNT); \ + using(float __usage__ = 0, ({ \ + if (0 == SAFE_NAME(s_wLoopCounter)) { \ + __usage__ = (float)((double)SAFE_NAME(s_lTotal) \ + / (double)( get_system_ticks() \ + - SAFE_NAME(s_lTimestamp))); \ + __usage__ *= 100.0f; \ + SAFE_NAME(s_lTimestamp) = 0; \ + SAFE_NAME(s_lTotal) = 0; \ + if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) { \ + __perf_counter_printf__("CPU Usage %3.2f%%\r\n", (double)__usage__);\ + } else { \ + __VA_ARGS__ \ + } \ + } \ + if (0 == SAFE_NAME(s_lTimestamp)) { \ + SAFE_NAME(s_lTimestamp) = get_system_ticks(); \ + SAFE_NAME(s_wLoopCounter) = (__CNT); \ + } \ + start_task_cycle_counter();}), \ + ({SAFE_NAME(s_lTotal) += stop_task_cycle_counter(); \ + SAFE_NAME(s_wLoopCounter)--;})) + +#define __cpu_time__ __cpu_usage__ + +/*! + * \addtogroup gBasicTimerService 1.2 Timer Service + * \ingroup gBasic + * @{ + */ + +/*! + * \brief should not use + */ +#define ticks_is_time_out_ms0() true + +/*! + * \brief set an alarm with given period in ms and check the status + * + * \param[in] __ms a time period in millisecond + * \param[in] __timestamp_ptr an optional timestamp holder + * \param[in] __auto_reload whether starting next period after a timeout event + * + * \return bool whether it is timeout + */ +#define ticks_is_time_out_ms3(__ms, __timestamp_ptr, __auto_reload) \ + ({ static int64_t SAFE_NAME(s_lTimestamp); (void)SAFE_NAME(s_lTimestamp); \ + __ticks_is_time_out(ticks_convert_ms_to_ticks(__ms), \ + (__timestamp_ptr), (__auto_reload));}) + +/*! + * \brief set an alarm with given period in ms and check the status + * + * \param[in] __ms a time period in millisecond + * \param[in] __timestamp_ptr an optional timestamp holder + * + * \return bool whether it is timeout + */ +#define ticks_is_time_out_ms2(__ms, __timestamp_ptr) \ + ticks_is_time_out_ms3((__ms), (__timestamp_ptr), true) + + +/*! + * \brief set an alarm with given period in ms and check the status + * + * \param[in] __ms a time period in millisecond + * \param[in] __timestamp_ptr an optional timestamp holder + * + * \return bool whether it is timeout + */ +#define ticks_is_time_out_ms1(__ms) \ + ticks_is_time_out_ms3((__ms), &SAFE_NAME(s_lTimestamp), true) + +/*! + * \brief set an alarm with given period in ms and check the status + * + * \param[in] __ms a time period in millisecond + * \param[in] ... an optional timestamp holder + * \param[in] ... an optional indicator for whether starting next period after a timeout event + * + * \return bool whether it is timeout + */ +#define ticks_is_time_out_ms(...) \ + CONNECT2(ticks_is_time_out_ms, __PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ + (__VA_ARGS__) + +/*! + * \brief set an alarm with given period in us and check the status + * + * \param[in] __us a time period in microsecond + * \param[in] __timestamp_ptr an optional timestamp holder + * \param[in] __auto_reload whether starting next period after a timeout event + * + * \return bool whether it is timeout + */ +#define ticks_is_time_out_us3(__us, __timestamp_ptr, __auto_reload) \ + ({ static int64_t SAFE_NAME(s_lTimestamp); (void)SAFE_NAME(s_lTimestamp); \ + __ticks_is_time_out(ticks_convert_us_to_ticks(__us), \ + (__timestamp_ptr), (__auto_reload));}) + +/*! + * \brief set an alarm with given period in us and check the status + * + * \param[in] __us a time period in microsecond + * \param[in] __timestamp_ptr an optional timestamp holder + * + * \return bool whether it is timeout + */ +#define ticks_is_time_out_us2(__us, __timestamp_ptr) \ + ticks_is_time_out_us3((__us), (__timestamp_ptr), true) + + +/*! + * \brief set an alarm with given period in us and check the status + * + * \param[in] __us a time period in microsecond + * \param[in] __timestamp_ptr an optional timestamp holder + * + * \return bool whether it is timeout + */ +#define ticks_is_time_out_us1(__us) \ + ticks_is_time_out_us3((__us), &SAFE_NAME(s_lTimestamp), true) + +/*! + * \brief set an alarm with given period in us and check the status + * + * \param[in] __us a time period in microsecond + * \param[in] ... an optional timestamp holder + * \param[in] ... an optional indicator for whether starting next period after a timeout event + * + * \return bool whether it is timeout + */ +#define ticks_is_time_out_us(...) \ + CONNECT2(ticks_is_time_out_us, __PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ + (__VA_ARGS__) + +/*============================ TYPES =========================================*/ +typedef struct { + int64_t lStart; + int64_t lUsedTotal; + int32_t nUsedRecent; + uint16_t hwActiveCount; + uint16_t : 15; + uint16_t bEnabled: 1; +} task_cycle_info_t; + +typedef struct task_cycle_info_agent_t task_cycle_info_agent_t; + +struct task_cycle_info_agent_t { + task_cycle_info_t *ptInfo; + task_cycle_info_agent_t *ptNext; + task_cycle_info_agent_t *ptPrev; +}; + +extern volatile int64_t g_lLastTimeStamp; +extern volatile int32_t g_nOffset; + + +__attribute__((noinline)) +extern int64_t get_system_ticks(void); + + +#ifdef __TICKS_USE_LONG_CLOCK__ +/*! \note the prototype of this clock() is different from the one defined in + *! time.h. As clock_t is usually defined as unsigned int, it is + *! not big enough in Cortex-M system to hold a time-stamp. clock() + *! defined here returns the timestamp since the begining of main() + *! and its unit is clock cycle (rather than 1ms). Hence, for a system + *! running under several hundreds MHz or even 1GHz, e.g. RT10xx from + *! NXP, it is very easy to see a counter overflow as clock_t is + *! defined as uint32_t in timer.h. + *! Since we are not allowed to change the defintion of clock_t in + *! official header file, i.e. time.h, I use a compatible prototype + *! after I checked the AAPCS spec. So, the return of the clock() is + *! int64_t, which will use the R0 to store the lower 32bits and R1 + *! to store the higher 32bits. When you are using the prototype from + *! timer.h, caller will only take the lower 32bits stored in R0 and + *! the higher 32bits stored in R1 will be ignored. + *! + *! If you want to use the non-overflow version of this clock(), please + *! 1) define the MACRO: __PERF_CNT_USE_LONG_CLOCK__ in your project + *! and 2) do not include system header file + *! + */ +#if !defined(__IS_COMPILER_IAR__) +__attribute__((nothrow)) +#endif +__attribute__((noinline)) +extern int64_t clock(void); +#endif + +/*! + * \brief try to set a start pointer for the performance counter + */ +static inline +void start_cycle_counter(void) { + g_lLastTimeStamp = get_system_ticks(); +} + +/*! + * \brief calculate the elapsed cycle count since the last start point + * \note you can have multiple stop_cycle_counter following one start point + * \return int32_t the elapsed cycle count + */ +static inline +int64_t stop_cycle_counter(void) { + int64_t lTemp = (get_system_ticks() - g_lLastTimeStamp); + + return lTemp - g_nOffset; +} + + + + +/*! @} */ + + +/*! + * \addtogroup gBasicTimerService 1.2 Timer Service + * \ingroup gBasic + * @{ + */ + +/*! + * \brief get the system timer frequency + * \return uint32_t the system timer frequency in Hz + */ +extern uint32_t ticks_get_systimer_frequency(void); + +/*! + * \brief get the elapsed milliseconds since perf_counter is initialised + * \return int64_t the elapsed milliseconds + */ +extern int64_t get_system_ms(void); + +/*! + * \brief get the elapsed microsecond since perf_counter is initialised + * \return int64_t the elapsed microsecond + */ +extern int64_t get_system_us(void); + +/*! + * \brief delay specified time in microsecond + * \param[in] wUs time in microsecond + */ +extern void delay_us(uint32_t wUs); + +/*! + * \brief delay specified time in millisecond + * \param[in] wMs time in millisecond + */ +extern void delay_ms(uint32_t nMs); + +/*! + * \brief convert ticks of a reference timer to millisecond + * + * \param[in] lTick the tick count + * \return int64_t the millisecond + */ +extern +int64_t ticks_convert_ticks_to_ms(int64_t lTick); + +/*! + * \brief convert millisecond into ticks of the reference timer + * + * \param[in] wMS the target time in millisecond + * \return int64_t the ticks + */ +extern +int64_t ticks_convert_ms_to_ticks(uint32_t wMS); + +/*! + * \brief convert ticks of a reference timer to microsecond + * + * \param[in] lTick the tick count + * \return int64_t the microsecond + */ +extern +int64_t ticks_convert_ticks_to_us(int64_t lTick); + +/*! + * \brief convert microsecond into ticks of the reference timer + * + * \param[in] wUS the target time in microsecond + * \return int64_t the ticks + */ +extern +int64_t ticks_convert_us_to_ticks(uint32_t wUS); + +/*! + * \brief set an alarm with given period and check the status + * + * \param[in] lPeriod a time period in ticks + * \param[in] plTimestamp a pointer points to an int64_t integer, if NULL is + * passed, an static local variable inside the function will be used + * \param[in] bAutoReload whether starting next period after a timeout event. + * \return bool whether it is timeout or not + */ +extern +bool __ticks_is_time_out(int64_t lPeriod, int64_t *plTimestamp, bool bAutoReload); + +/*! + * \addtogroup gBasic 1 Basic + * @{ + */ + +/*----------------------------------------------------------------------------* + * Please ignore the following APIs unless you have encountered some known * + * special conditions * + *----------------------------------------------------------------------------*/ + +/*! \brief initialise cycle counter service + * \note - don't forget to tell the function whether the systick is already + * used by user applications. + * Don't worry, this cycle counter service won't affect your existing + * systick service. + * + * \note - Usually the perf_counter can initialise itself with the help of + * __attribute__((constructor(255))), this works fine in Arm Compiler + * 5 (armcc), Arm Compiler 6 (armclang), arm gcc and llvm. It doesn't + * work for IAR. So, when you are using IAR, please call this function + * manually to initialise the perf_counter service. + * + * \note - Perf_counter library assumes that: + * 1. Your project has already using SysTick + * 2. It assumes that you have already implemented the SysTick_Handler + * 3. It assumes that you have enabled the exception handling for + * SysTick. + * If these are not the case, please: + * 1. Add an empty SysTick_Handler to your project if you don't have + * one + * 2. Make sure you have the SysTick Exception handling enabled + * 3. And call function init_cycle_counter(false) if you doesn't + * use SysTick in your project at all. + * + * \param[in] bIsSysTickOccupied A boolean value which indicates whether SysTick + * is already used by user application. + * + * \return false Failed to initialize the timer counter, as the timer is not + * available or IO error. + * \return true initialization is successful. + */ +extern bool init_cycle_counter(bool bIsSysTickOccupied); + + +/*! + * \brief a system timer overflow handler + * + * \note - if you are using a compiler other than armcc or armclang, e.g. iar, + * arm gcc etc, the systick_wrapper_ual.o doesn't work with the linker + * of your target toolchain as it use the $Super$$ which is only supported + * by armlink. For this condition, you have to manually put this function + * into your existing SysTick_Handler to make the perf_counter library + * work. + * + * \note - if you are using Arm Compiler 5 (armcc) or Arm Compiler 6 (armclang) + * you do NOT have to insert this function into your SysTick_Handler, + * the systick_wrapper_ual.s will do the work for you. + */ +extern void ticks_port_insert_to_system_timer_insert_ovf_handler(void); + +/*! + * \brief update perf_counter as SystemCoreClock has been updated. + */ +extern void update_perf_counter(void); + +/*! + * \brief prepare for reconfiguration of SysTick timer. + * + * \note some systems (e.g. FreeRTOS) might reconfigure the systick timer to + * fulfil the requirement of their feature. To support this, just + * before the reconfiguration, please call this function in order + * to make the perf_counter works correctly later. + * + * \note after the reconfiguration, please call update_perf_counter() to apply + * the changes to perf_counter. + * + * \note this function will stop the SysTick, clear the pending bit and set + * the Load register and Current Value register to zero. + */ +extern void before_cycle_counter_reconfiguration(void); + + +#ifndef __ticks_sync_barrier__ +# define __ticks_sync_barrier__(...) do {__DSB();__ISB();} while(0) +#endif + +typedef uint32_t ticks_global_interrupt_status_t; + +__STATIC_INLINE +ticks_global_interrupt_status_t ticks_port_disable_global_interrupt(void); +//{ +// ticks_global_interrupt_status_t tStatus = __get_PRIMASK(); +// __disable_irq(); +// +// return tStatus; +//} + +__STATIC_INLINE +void ticks_port_resume_global_interrupt(ticks_global_interrupt_status_t tStatus); +//{ +// __set_PRIMASK(tStatus); +//} + + +//#if defined(__clang__) +//# pragma clang diagnostic pop +//#elif defined(__IS_COMPILER_GCC__) +//# pragma GCC diagnostic pop +//#endif + +#ifdef __cplusplus +} +#endif +#endif //HW_LIB_TICKS_H diff --git a/lib/utils/inc/tool.h b/lib/utils/inc/tool.h index a1a5cf8..1a40110 100644 --- a/lib/utils/inc/tool.h +++ b/lib/utils/inc/tool.h @@ -48,9 +48,9 @@ typedef enum { // 定义枚举类型Type_t,包含不同数据类型 default: ((void)0)) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) // 计算数组的元素个数 -//#define in , // 定义逗号为in + #define _foreach(e, a) for(size_t e = 0; e < ARRAY_SIZE(a); e++) // 实现foreach宏,遍历数组a,e为当前元素下标 -#define foreach(exp) _foreach(exp) // 定义foreach宏,用于遍历数组 + #define _VA_ARGS_N(_9, _8, _7, _6, _5, _4, _3, _2, _1, _0, N, ...) N #define VA_ARGS_N(...) _VA_ARGS_N(0 __VA_OPT__(,) __VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) @@ -60,6 +60,7 @@ typedef enum { // 定义枚举类型Type_t,包含不同数据类型 #define _range_n_cat(a, b) a ## b #define _range_n(n) _range_n_cat(_range_, n) #define _range(...) _range_n(VA_ARGS_N(__VA_ARGS__)) // 根据传入参数个数选择对应的范围循环宏 + /** * @Name 范围循环 * @brief 根据传入参数个数生成不同范围的遍历 @@ -125,7 +126,7 @@ float Str2Float(char *str); * @param fmt: [输入] 格式化字段 * @param frame: [输入] 断帧大小 * @return void - * @example BufPrint("TX", buf, 8, 64, 16); //将长64的8位buf以16个数据断帧打印 + * @example PRINT_ARRAY(buf, " 0x%02x", 16); //将长64的8位buf以16个数据断帧打印 **/ #define PRINT_ARRAY(arr, fmt, frame) do { \ printf("\n"#arr ":\n"); \ @@ -143,9 +144,41 @@ int :"[int]-> "#v"=%d" END, \ float :"[float]-> "#v"=%.2f" END , \ double :"[double]-> "#v"=%.2f" END, \ default: "[err]-> "#v"=%p" END) + #define POUT(s) printf(TYPE_F(s) ,s) +#ifndef GET_BIT #define GET_BIT(x, bit) ((x & (1 << bit)) >> bit) +#endif +#ifndef SWAP +#ifdef __cplusplus +#define SWAP(x, y) do { \ + decltype(x) temp = x; \ + x = y; \ + y = temp; \ + } while(0) +#else +#define SWAP(x, y) do { \ + typeof(x) temp = x; \ + x = y; \ + y = temp; \ + } while(0) +#endif +#endif + +#ifndef MIN +# define MIN(__a, __b) ((__a) <= (__b) ? (__a) : (__b)) +#endif + +#ifndef MAX +# define MAX(__a, __b) ((__a) >= (__b) ? (__a) : (__b)) +#endif + +#ifndef __WEAK +# define __WEAK __attribute__((weak)) +#endif + + #ifdef __cplusplus } diff --git a/lib/utils/ticks.cpp b/lib/utils/ticks.cpp new file mode 100644 index 0000000..f356e60 --- /dev/null +++ b/lib/utils/ticks.cpp @@ -0,0 +1,631 @@ +#include +#include +#include + +#define __IMPLEMENT_PERF_COUNTER + +#include "ticks.h" + +#if defined(__IS_COMPILER_GCC__) +# pragma GCC diagnostic ignored "-Wattributes" +#endif + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunknown-warning-option" +# pragma clang diagnostic ignored "-Wreserved-identifier" +# pragma clang diagnostic ignored "-Wconditional-uninitialized" +# pragma clang diagnostic ignored "-Wcast-align" +# pragma clang diagnostic ignored "-Wmissing-prototypes" +#endif + + +/*============================ MACROS ========================================*/ +#ifndef PERF_CNT_COMPENSATION_THRESHOLD +# define PERF_CNT_COMPENSATION_THRESHOLD 16 +#endif + +#ifndef PERF_CNT_DELAY_US_COMPENSATION +# define PERF_CNT_DELAY_US_COMPENSATION 90 +#endif + +#define MAGIC_WORD_AGENT_LIST_VALID 0x8492A53C +#define MAGIC_WORD_CANARY 0xDEADBEEF + +/*============================ MACROFIED FUNCTIONS ===========================*/ +/*============================ TYPES =========================================*/ + +struct __task_cycle_info_t { + task_cycle_info_t tInfo; //!< cycle information + int64_t lLastTimeStamp; //!< previous timestamp + task_cycle_info_agent_t tList; //!< the root of the agent list + uint32_t wMagicWord; //!< an magic word for validation +}; + + +/*============================ GLOBAL VARIABLES ==============================*/ +/*============================ LOCAL VARIABLES ===============================*/ +volatile int64_t g_lLastTimeStamp = 0; +volatile static int64_t s_lOldTimestamp; +volatile int32_t g_nOffset = 0; +volatile static uint32_t s_wUSUnit = 1; +volatile static uint32_t s_wMSUnit = 1; +volatile static uint32_t s_wMSResidule = 0; +volatile static uint32_t s_wUSResidule = 0; +volatile static int64_t s_lSystemMS = 0; +volatile static int64_t s_lSystemUS = 0; + +volatile static int64_t s_lSystemClockCounts = 0; + +/*============================ PROTOTYPES ====================================*/ + +/* low level interface for porting */ +extern +uint32_t ticks_port_get_system_timer_freq(void); + +extern +int64_t ticks_port_get_system_timer_top(void); + +extern +bool ticks_port_is_system_timer_ovf_pending(void); + +extern +bool ticks_port_init_system_timer(bool bTimerOccupied); + +extern +int64_t ticks_port_get_system_timer_elapsed(void); + +extern +void ticks_port_clear_system_timer_ovf_pending(void); + +extern +void ticks_port_stop_system_timer_counting(void); + +extern +void ticks_port_clear_system_timer_counter(void); + +/*============================ INCLUDES ======================================*/ + +void ticks_port_insert_to_system_timer_insert_ovf_handler(void) { + int64_t lLoad = ticks_port_get_system_timer_top() + 1; + s_lSystemClockCounts += lLoad; + + // update system ms counter + do { + int64_t lTemp = s_wMSResidule + lLoad; + + int64_t lMS = lTemp / s_wMSUnit; + s_lSystemMS += lMS; + s_wMSResidule = (uint32_t) ((int64_t) lTemp - (int64_t) lMS * s_wMSUnit); + + } while (0); + + // update system us counter + do { + int64_t lTemp = s_wUSResidule + lLoad; + + int64_t lUS = lTemp / s_wUSUnit; + s_lSystemUS += lUS; + + s_wUSResidule = (uint32_t) ((int64_t) lTemp - (int64_t) lUS * s_wUSUnit); + + } while (0); + +} + +uint32_t ticks_get_systimer_frequency(void) { + return ticks_port_get_system_timer_freq(); +} + +__WEAK +void __perf_os_patch_init(void) { +} + + +void update_perf_counter(void) { + uint32_t wSystemFrequency = ticks_port_get_system_timer_freq(); + s_wUSUnit = wSystemFrequency / 1000000ul; + s_wMSUnit = wSystemFrequency / 1000ul; + + __IRQ_SAFE { + g_lLastTimeStamp = get_system_ticks(); + __ticks_sync_barrier__(); + g_nOffset = get_system_ticks() - g_lLastTimeStamp; + } +} + +bool init_cycle_counter(bool bIsSysTickOccupied) { + bool bResult = false; + __IRQ_SAFE { + bResult = ticks_port_init_system_timer(bIsSysTickOccupied); // use the longest period + ticks_port_clear_system_timer_ovf_pending(); + } + + update_perf_counter(); + s_lSystemClockCounts = 0; // reset system cycle counter + s_lSystemMS = 0; // reset system millisecond counter + s_lSystemUS = 0; // reset system microsecond counter + s_lOldTimestamp = 0; + + __perf_os_patch_init(); + + return bResult; +} + +/*! \note this function should only be called when irq is disabled + * hence SysTick-LOAD and (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk) + * won't change. + */ +__STATIC_INLINE int64_t check_systick(void) { + int64_t lLoad = ticks_port_get_system_timer_top() + 1; + int64_t lTemp = ticks_port_get_system_timer_elapsed(); + + /* Since we cannot stop counting temporarily, there are several + * conditions which we should take into consideration: + * - Condition 1: when assigning nTemp with the register value (LOAD-VAL), + * the underflow didn't happen but when we check the PENDSTSET bit, + * the underflow happens, for this condition, we should not + * do any compensation. When this happens, the (LOAD-nTemp) is + * smaller than PERF_CNT_COMPENSATION_THRESHOLD (a small value) as + * long as LOAD is bigger than (or equals to) the + * PERF_CNT_COMPENSATION_THRESHOLD; + * - Condition 2: when assigning nTemp with the register value (LOAD-VAL), + * the VAL is zero and underflow happened and the PENDSTSET bit + * is set, for this condition, we should not do any compensation. + * When this happens, the (LOAD-nTemp) is equals to zero. + * - Condition 3: when assigning nTemp with the register value (LOAD-VAL), + * the underflow has already happened, hence the PENDSTSET + * is set, for this condition, we should compensate the return + * value. When this happens, the (LOAD-nTemp) is bigger than (or + * equals to) PERF_CNT_COMPENSATION_THRESHOLD. + * The following code implements an equivalent logic. + */ + if (ticks_port_is_system_timer_ovf_pending()) { + if ((lLoad - lTemp) >= PERF_CNT_COMPENSATION_THRESHOLD) { + lTemp += lLoad; + } + } + + return lTemp; +} + +void before_cycle_counter_reconfiguration(void) { + __IRQ_SAFE { + ticks_port_stop_system_timer_counting(); + + if (ticks_port_is_system_timer_ovf_pending()) { + ticks_port_clear_system_timer_ovf_pending(); /* clear pending bit */ + + ticks_port_insert_to_system_timer_insert_ovf_handler(); /* manually handle exception */ + + } + s_lSystemClockCounts = get_system_ticks(); /* get the final cycle counter value */ + + ticks_port_clear_system_timer_counter(); + } +} + +__attribute__((constructor)) +void __perf_counter_init(void) { + init_cycle_counter(true); +} + + +void delay_us(uint32_t wUs) { + int64_t lUs = (int64_t) wUs * (int64_t) s_wUSUnit; + int32_t iCompensate = g_nOffset > PERF_CNT_DELAY_US_COMPENSATION + ? g_nOffset + : PERF_CNT_DELAY_US_COMPENSATION; + + if (lUs <= iCompensate) { + return; + } + + lUs -= iCompensate; + + lUs += get_system_ticks(); + while (get_system_ticks() < lUs); +} + +void delay_ms(uint32_t wMs) { + int64_t lMs = (int64_t) wMs * (int64_t) s_wMSUnit; + int32_t iCompensate = g_nOffset > PERF_CNT_DELAY_US_COMPENSATION + ? g_nOffset + : PERF_CNT_DELAY_US_COMPENSATION; + + if (lMs <= iCompensate) { + return; + } + + lMs -= iCompensate; + + lMs += get_system_ticks(); + while (get_system_ticks() < lMs); +} + +__attribute__((noinline)) +int64_t get_system_ticks(void) { + int64_t lTemp = 0; + + __IRQ_SAFE { + lTemp = check_systick() + s_lSystemClockCounts; + + /* When calling get_system_ticks() in an exception handler that has a + * higher priority than the SysTick_Handler, in some rare cases, the + * lTemp might be temporarily smaller than the previous value (i.e. + * s_lOldTimestamp), to mitigate the adverse effects of this problem, + * we use the following code to avoid time-rolling-back issue. + * + * NOTE: the issue mentioned above doesn't accumulate or have long-lasting + * effects. + */ + if (lTemp < s_lOldTimestamp) { + lTemp = s_lOldTimestamp; + } else { + s_lOldTimestamp = lTemp; + } + } + + return lTemp; +} + +/*! \note the prototype of this clock() is different from the one defined in + *! time.h. As clock_t is usually defined as unsigned int, it is + *! not big enough in Cortex-M system to hold a time-stamp. clock() + *! defined here returns the timestamp since the begining of main() + *! and its unit is clock cycle (rather than 1ms). Hence, for a system + *! running under several hundreds MHz or even 1GHz, e.g. RT10xx from + *! NXP, it is very easy to see a counter overflow as clock_t is + *! defined as uint32_t in timer.h. + *! Since we are not allowed to change the defintion of clock_t in + *! official header file, i.e. time.h, I use a compatible prototype + *! after I checked the AAPCS spec. So, the return of the clock() is + *! int64_t, which will use the R0 to store the lower 32bits and R1 + *! to store the higher 32bits. When you are using the prototype from + *! timer.h, caller will only take the lower 32bits stored in R0 and + *! the higher 32bits stored in R1 will be ignored. + *! + *! If you want to use the non-overflow version of this clock(), please + *! 1) define the MACRO: __PERF_CNT_USE_LONG_CLOCK__ in your project + *! and 2) do not include system header file + *! + */ +#if !defined(__IS_COMPILER_IAR__) + +__attribute__((nothrow)) +#endif +__attribute__((noinline)) +int64_t clock(void) { + return get_system_ticks(); +} + +int64_t get_system_ms(void) { + int64_t lTemp = 0; + + __IRQ_SAFE { + lTemp = s_lSystemMS + ((check_systick() + (int64_t) s_wMSResidule) / s_wMSUnit); + } + + return lTemp; +} + +int64_t get_system_us(void) { + int64_t lTemp = 0; + + __IRQ_SAFE { + lTemp = s_lSystemUS + ((check_systick() + (int64_t) s_wUSResidule) / s_wUSUnit); + } + + return lTemp; +} + +int64_t ticks_convert_ticks_to_ms(int64_t lTick) { + return lTick / (int64_t) s_wMSUnit; +} + +int64_t ticks_convert_ms_to_ticks(uint32_t wMS) { + int64_t lResult = (int64_t) s_wMSUnit * (int64_t) wMS; + return lResult ? lResult : 1; +} + +int64_t ticks_convert_ticks_to_us(int64_t lTick) { + return lTick / (int64_t) s_wUSUnit; +} + +int64_t ticks_convert_us_to_ticks(uint32_t wMS) { + int64_t lResult = (int64_t) s_wUSUnit * (int64_t) wMS; + return lResult ? lResult : 1; +} + + +bool __ticks_is_time_out(int64_t lPeriod, int64_t *plTimestamp, bool bAutoReload) { + if (NULL == plTimestamp) { + return false; + } + + int64_t lTimestamp = get_system_ticks(); + + + if (0 == *plTimestamp) { + *plTimestamp = lPeriod; + *plTimestamp += lTimestamp; + + return false; + } + + if (lTimestamp >= *plTimestamp) { + if (bAutoReload) { + *plTimestamp = lPeriod + lTimestamp; + } + return true; + } + + return false; +} + + +/// Setup timer hardware. +/// \return status (1=Success, 0=Failure) +uint32_t EventRecorderTimerSetup(void) { + /* doing nothing at all */ + return 1; +} + +/// Get timer frequency. +/// \return timer frequency in Hz +uint32_t EventRecorderTimerGetFreq(void) { + return ticks_port_get_system_timer_freq(); +} + +/// Get timer count. +/// \return timer count (32-bit) +uint32_t EventRecorderTimerGetCount(void) { + return get_system_ticks(); +} + + +__WEAK +task_cycle_info_t *get_rtos_task_cycle_info(void) { + return NULL; +} + +void init_task_cycle_counter(void) { + struct __task_cycle_info_t *ptRootAgent = + (struct __task_cycle_info_t *) get_rtos_task_cycle_info(); + if (NULL == ptRootAgent) { + return; + } + + memset(ptRootAgent, 0, sizeof(struct __task_cycle_info_t)); + + ptRootAgent->tList.ptInfo = &(ptRootAgent->tInfo); + ptRootAgent->tInfo.lStart = get_system_ticks(); + ptRootAgent->wMagicWord = MAGIC_WORD_CANARY; +} + +bool ticks_check_task_stack_canary_safe(void) { + struct __task_cycle_info_t *ptRootAgent = + (struct __task_cycle_info_t *) get_rtos_task_cycle_info(); + do { + if (NULL == ptRootAgent) { + break; + } + + if ((MAGIC_WORD_CANARY == ptRootAgent->wMagicWord) + || (MAGIC_WORD_AGENT_LIST_VALID == ptRootAgent->wMagicWord)) { + return true; + } + } while (0); + + return false; +} + +task_cycle_info_t *init_task_cycle_info(task_cycle_info_t *ptInfo) { + do { + if (NULL == ptInfo) { + break; + } + + memset(ptInfo, 0, sizeof(task_cycle_info_t)); + + ptInfo->bEnabled = true; + } while (0); + + return ptInfo; +} + +bool enable_task_cycle_info(task_cycle_info_t *ptInfo) { + if (NULL == ptInfo) { + return false; + } + bool bOrig; + __IRQ_SAFE { + bOrig = ptInfo->bEnabled; + ptInfo->bEnabled = true; + } + return bOrig; +} + +bool disable_task_cycle_info(task_cycle_info_t *ptInfo) { + if (NULL == ptInfo) { + return false; + } + bool bOrig; + __IRQ_SAFE { + bOrig = ptInfo->bEnabled; + ptInfo->bEnabled = false; + } + return bOrig; +} + +void resume_task_cycle_info(task_cycle_info_t *ptInfo, bool bEnabledStatus) { + if (NULL == ptInfo) { + return; + } + + ptInfo->bEnabled = bEnabledStatus; +} + + +task_cycle_info_agent_t *register_task_cycle_agent(task_cycle_info_t *ptInfo, + task_cycle_info_agent_t *ptAgent) { + __IRQ_SAFE { + do { + if (NULL == ptAgent || NULL == ptInfo) { + break; + } + + struct __task_cycle_info_t *ptRootAgent = + (struct __task_cycle_info_t *) get_rtos_task_cycle_info(); + if (NULL == ptRootAgent) { + break; + } + + ptRootAgent->wMagicWord = MAGIC_WORD_AGENT_LIST_VALID; + + ptAgent->ptInfo = ptInfo; + + // push to the stack + do { + // set next-list + ptAgent->ptNext = ptRootAgent->tList.ptNext; + ptRootAgent->tList.ptNext = ptAgent; + + // set prev-list + ptAgent->ptPrev = &(ptRootAgent->tList); + if (NULL != ptAgent->ptNext) { + ptAgent->ptNext->ptPrev = ptAgent; + } + } while (0); + + } while (0); + } + + return ptAgent; +} + +task_cycle_info_agent_t * +unregister_task_cycle_agent(task_cycle_info_agent_t *ptAgent) { + __IRQ_SAFE { + do { + if (NULL == ptAgent) { + break; + } + + task_cycle_info_agent_t *ptPrev = ptAgent->ptPrev; + if (NULL == ptPrev) { + break; /* this should not happen */ + } + if (ptPrev->ptNext != ptAgent) { + // already removed + break; + } + + //! remove agent from the next-list + ptPrev->ptNext = ptAgent->ptNext; + + if (NULL != ptAgent->ptNext) { + // remove agent from the prev-list + ptAgent->ptNext->ptPrev = ptPrev; + } + + ptAgent->ptNext = NULL; + ptAgent->ptPrev = NULL; + + } while (0); + } + + return ptAgent; +} + + +void __on_context_switch_in(uint32_t *pwStack) { + struct __task_cycle_info_t *ptRootAgent = (struct __task_cycle_info_t *) pwStack; + int64_t lTimeStamp = get_system_ticks(); + + ptRootAgent->lLastTimeStamp = lTimeStamp; + ptRootAgent->tInfo.hwActiveCount++; + + if (MAGIC_WORD_AGENT_LIST_VALID == ptRootAgent->wMagicWord) { + // update all agents + task_cycle_info_agent_t *ptAgent = ptRootAgent->tList.ptNext; + while (NULL != ptAgent) { + if (NULL != ptAgent->ptInfo) { + if (ptAgent->ptInfo->bEnabled) { + ptAgent->ptInfo->hwActiveCount++; + } + } + ptAgent = ptAgent->ptNext; + } + } +} + +void __on_context_switch_out(uint32_t *pwStack) { + struct __task_cycle_info_t *ptRootAgent = (struct __task_cycle_info_t *) pwStack; + int64_t lCycleUsed = get_system_ticks() - ptRootAgent->lLastTimeStamp - g_nOffset; + + ptRootAgent->tInfo.nUsedRecent = lCycleUsed; + ptRootAgent->tInfo.lUsedTotal += lCycleUsed; + + if (MAGIC_WORD_AGENT_LIST_VALID == ptRootAgent->wMagicWord) { + // update all agents + task_cycle_info_agent_t *ptAgent = ptRootAgent->tList.ptNext; + while (NULL != ptAgent) { + if (NULL != ptAgent->ptInfo) { + if (ptAgent->ptInfo->bEnabled) { + ptAgent->ptInfo->nUsedRecent = lCycleUsed; + ptAgent->ptInfo->lUsedTotal += lCycleUsed; + } + } + ptAgent = ptAgent->ptNext; + } + } +} + +__attribute__((noinline)) +void __start_task_cycle_counter(task_cycle_info_t *ptInfo) { + struct __task_cycle_info_t *ptRootAgent = + (struct __task_cycle_info_t *) get_rtos_task_cycle_info(); + if (NULL == ptRootAgent) { + return; + } + + __IRQ_SAFE { + ptRootAgent->lLastTimeStamp = get_system_ticks(); + ptRootAgent->tInfo.lUsedTotal = 0; + + if (NULL != ptInfo) { + ptInfo->lUsedTotal = 0; + ptInfo->bEnabled = true; + } + } +} + +__attribute__((noinline)) +int64_t __stop_task_cycle_counter(task_cycle_info_t *ptInfo) { + struct __task_cycle_info_t *ptRootAgent = + (struct __task_cycle_info_t *) get_rtos_task_cycle_info(); + if (NULL == ptRootAgent) { + return 0; + } + + int64_t lCycles = 0; + + __IRQ_SAFE { + int64_t lCycleUsed = get_system_ticks() - ptRootAgent->lLastTimeStamp - g_nOffset; + ptRootAgent->tInfo.lUsedTotal += lCycleUsed; + + if (NULL != ptInfo) { + if (ptInfo->bEnabled) { + ptInfo->nUsedRecent = lCycleUsed; + ptInfo->lUsedTotal += lCycleUsed; + ptInfo->bEnabled = false; + } + + lCycles = ptInfo->lUsedTotal; + } else { + lCycles = ptRootAgent->tInfo.lUsedTotal; + } + } + + return lCycles; +} diff --git a/main.c b/main.c index c2fd844..6a1a72d 100644 --- a/main.c +++ b/main.c @@ -76,14 +76,14 @@ int main(int argc, char *argv[]) { // return -1; // } // SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); -// SDL_Rect rect1 = { 1, 1, 500, 500 }; +// SDL_FRect rect1 = { 1, 1, 500, 500 }; // SDL_RenderFillRect(renderer, &rect1); // // SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE); // // SDL_RenderLines(renderer, points, POINTS_COUNT); // -// SDL_Rect rect = { 200, 300, 100, 100 }; +// SDL_FRect rect = { 200, 300, 100, 100 }; // SDL_RenderRect(renderer, &rect); // // SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255); @@ -166,9 +166,9 @@ int main(int argc, char *argv[]) { // Test_Run("List", Test_List,NULL); // Test_RunTime("Key", Test_Key); // Test_RunTime("Queue", Test_Queue); -// Test_RunTime("Task", Test_task); + Test_RunTime("Task", Test_task); // Test_RunTime("OLED", Test_OLED); - Test_RunTime("LVGL", Test_lvgl); +// Test_RunTime("LVGL", Test_lvgl); // Test_RunTime("TFT", Test_tft); return 0; } diff --git a/sim/display/sim_display.cpp b/sim/display/sim_display.cpp index 09f8068..c84bd11 100644 --- a/sim/display/sim_display.cpp +++ b/sim/display/sim_display.cpp @@ -170,11 +170,14 @@ bool SIM_Display_Init(char *name, int width, int height, uint32_t pixcolor, uint void SIM_Color_DrawPiexl(SIM_Display_t *display, SIM_Color_t color, uint16_t x, uint16_t y) { SDL_SetRenderDrawColor(display->renderer, color.ch.red, color.ch.green, color.ch.blue, color.ch.alpha); - for (int y_ = y * display->scale; y_ < (y * display->scale) + display->scale; ++y_) { - for (int x_ = x * display->scale; x_ < (x * display->scale) + display->scale; ++x_) { - SDL_RenderPoint(display->renderer, x_, y_); - } - } + SDL_FRect rect1 = {(x * display->scale) * 1.0f, (y * display->scale) * 1.0f, display->scale * 1.0f, + display->scale * 1.0f}; + SDL_RenderFillRect(display->renderer, &rect1); +// for (int y_ = y * display->scale; y_ < (y * display->scale) + display->scale; ++y_) { +// for (int x_ = x * display->scale; x_ < (x * display->scale) + display->scale; ++x_) { +// SDL_RenderPoint(display->renderer, x_, y_); +// } +// } } void SIM_OneColor_DrawFromBuffer(SIM_Display_t *display, uint8_t *buf, uint16_t width, uint16_t height) { diff --git a/sim/key/key.cpp b/sim/key/key.cpp index a747eb8..0018c1f 100644 --- a/sim/key/key.cpp +++ b/sim/key/key.cpp @@ -1,6 +1,7 @@ #include "sim_key.h" #include "log.h" +#ifndef USER_SDL3 #include #include @@ -80,4 +81,31 @@ uint8_t SIM_Key_SET(uint8_t) { uint8_t SIM_Key_RESET(uint8_t) { return GetAsyncKeyState(VK_END); -} \ No newline at end of file +} +#else + +#include "SDL3/SDL.h" + +uint8_t SIM_Key_Scan() { + SDL_Event event; + uint8_t key = 0; + SDL_PollEvent(&event); + switch (event.type) { + case SDL_EVENT_QUIT: + return 0; + case SDL_EVENT_KEY_DOWN: + if (event.key.key == SDLK_Q)key = SIM_KEY_SET; + if (event.key.key == SDLK_UP || event.key.key == SDLK_W)key = SIM_KEY_UP; + if (event.key.key == SDLK_DOWN || event.key.key == SDLK_S)key = SIM_KEY_DOWN; + if (event.key.key == SDLK_LEFT || event.key.key == SDLK_A)key = SIM_KEY_LEFT; + if (event.key.key == SDLK_RIGHT || event.key.key == SDLK_D)key = SIM_KEY_RIGHT; + if (event.key.key == SDLK_R)key = SIM_KEY_RESET; + if (event.key.key == SDLK_KP_ENTER || event.key.key == SDLK_E)key = SIM_KEY_ENABLE; + break; + default: + break; + } + return key; +} + +#endif diff --git a/sim/key/sim_key.h b/sim/key/sim_key.h index 2be8b26..8f60010 100644 --- a/sim/key/sim_key.h +++ b/sim/key/sim_key.h @@ -5,6 +5,9 @@ extern "C" { #endif #include "stdint.h" +//是否启用SDL3获取按键事件 +#define USER_SDL3 + typedef enum Key_map { SIM_KEY_UP = 1, SIM_KEY_DOWN, diff --git a/sim/lvgl/lv_conf.h b/sim/lvgl/lv_conf.h index 23ab8f9..5e684b6 100644 --- a/sim/lvgl/lv_conf.h +++ b/sim/lvgl/lv_conf.h @@ -736,7 +736,7 @@ ====================*/ /*Show some widget. It might be required to increase `LV_MEM_SIZE` */ -#define LV_USE_DEMO_WIDGETS 0 +#define LV_USE_DEMO_WIDGETS 1 #if LV_USE_DEMO_WIDGETS #define LV_DEMO_WIDGETS_SLIDESHOW 0 #endif diff --git a/sim/lvgl/lv_port_disp.cpp b/sim/lvgl/lv_port_disp.cpp index d219280..76c68aa 100644 --- a/sim/lvgl/lv_port_disp.cpp +++ b/sim/lvgl/lv_port_disp.cpp @@ -91,17 +91,17 @@ void lv_port_disp_init(void) // lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ // /* Example for 2) */ - // static lv_disp_draw_buf_t draw_buf_dsc_2; - // static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/ - // static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/ - // lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ + static lv_disp_draw_buf_t draw_buf_dsc_2; + static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/ + static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/ + lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ /* Example for 3) also set disp_drv.full_refresh = 1 below*/ - static lv_disp_draw_buf_t draw_buf_dsc_3; - static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/ - static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/ - lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, - MY_DISP_VER_RES * MY_DISP_HOR_RES); /*Initialize the display buffer*/ +// static lv_disp_draw_buf_t draw_buf_dsc_3; +// static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/ +// static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/ +// lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, +// MY_DISP_VER_RES * MY_DISP_HOR_RES); /*Initialize the display buffer*/ /*----------------------------------- * Register the display in LVGL @@ -120,7 +120,7 @@ void lv_port_disp_init(void) disp_drv.flush_cb = disp_flush; /*Set a display buffer*/ - disp_drv.draw_buf = &draw_buf_dsc_3; + disp_drv.draw_buf = &draw_buf_dsc_2; /*Required for Example 3)*/ //disp_drv.full_refresh = 1; diff --git a/sim/sdl/sim_sdl.cpp b/sim/sdl/sim_sdl.cpp deleted file mode 100644 index 384ceaf..0000000 --- a/sim/sdl/sim_sdl.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "sim_sdl.h" - - -bool SIM_SDLInit(char *name, int width, int height, int scale, SIM_SDL3_t *sdl3) { - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - SDL_Log("SDL_Init failed: %s", SDL_GetError()); - return false; - } - sdl3->scale = scale; - sdl3->window = SDL_CreateWindow(name, width * scale, height * scale, SDL_WINDOW_RESIZABLE); - if (!sdl3->window) { - SDL_Log("Could not create a window: %s", SDL_GetError()); - return false; - } - SDL_SetWindowPosition(sdl3->window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); - sdl3->renderer = SDL_CreateRenderer(sdl3->window, nullptr); - if (!sdl3->renderer) { - SDL_Log("Create renderer failed: %s", SDL_GetError()); - return false; - } - return true; -} - -void SIM_SDL_Color_DrawPiexl(SIM_SDL3_t *sdl3, uint16_t x, uint16_t y, SIM_Color_t color) { - SDL_SetRenderDrawColor(sdl3->renderer, color.ch.red, color.ch.green, color.ch.blue, color.ch.alpha); - for (int y_ = y * sdl3->scale; y_ < (y * sdl3->scale) + sdl3->scale; ++y_) { - for (int x_ = x * sdl3->scale; x_ < (x * sdl3->scale) + sdl3->scale; ++x_) { - SDL_RenderPoint(sdl3->renderer, x_, y_); - } - } - SDL_RenderPresent(sdl3->renderer); -} - -void SIM_SDL_Stop(SIM_SDL3_t *sdl3) { - SDL_DestroyRenderer(sdl3->renderer); - SDL_DestroyWindow(sdl3->window); - SDL_Quit(); -} diff --git a/sim/sdl/sim_sdl.h b/sim/sdl/sim_sdl.h deleted file mode 100644 index 2af98ee..0000000 --- a/sim/sdl/sim_sdl.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef HW_LIB_SIM_SDL_H -#define HW_LIB_SIM_SDL_H - -#include "SDL3/SDL.h" - -#ifdef __cplusplus -extern "C" { -#endif -typedef struct { - SDL_Window *window; - SDL_Renderer *renderer; - SDL_Event *event; - int scale; -} SIM_SDL3_t; - -typedef union { - struct { - uint8_t blue; - uint8_t green; - uint8_t red; - uint8_t alpha; - } ch; - uint32_t full; -} SIM_Color_t; - - -bool SIM_SDLInit(char *name, int width, int height, int scale, SIM_SDL3_t *sdl3); - -void SIM_SDL_Color_DrawPiexl(SIM_SDL3_t *sdl3, uint16_t x, uint16_t y, SIM_Color_t color); - -void SIM_SDL_Stop(SIM_SDL3_t *sdl3); - -#ifdef __cplusplus -} -#endif -#endif //HW_LIB_SIM_SDL_H