嵌入式单片机开发中,通常使用 Keil IAR 或 芯片厂商提供的IDE,这类IDE界面风格还停留在十几年前XP风,代码编辑实在让人难受,所以很多时候笔者都是采用VSCode编辑,然后再用这类IDE下载调试使用,另外像keil 和 IAR 几万块价格也让很多人劝退(使用盗版,未收到律师函忽略)。那么有价格便宜,界面编辑优秀,又能跨平台的IDE吗?这里推荐VSCode(免费)和本章介绍的Clion
Clion环境搭建网上已经有很多文章出了教程,这里不做过多描述,本章主要介绍借助Clion平台开发某款芯片的流程。
配置CLion用于STM32开发【优雅の嵌入式开发】 - 知乎 (zhihu.com)
前期准备(文章末尾提供源码)
软件环境:
- Windows 10
- STM32CubeMX ------ ST官方代码生成工具(辅助使用,后期熟悉可脱离该工具)
- Clion-2022.3.2
- MinGW ------ 编译器套件
- OpenOCD ------ 调试工具
- arm-none-eabi-gcc -------- 交叉编译工具链
- 调试器驱动(具体看用的哪种调试器)
硬件环境:
- STM32L475VET6(正点原子潘多拉开发板)
- ST-Link (J-link /dap 都可以)
工程移植搭建
1.工程初步生成
这里有两种方式:借助STM32CubeMX,快速生成;自行下载库从零搭建。
推荐使用CubeMX,因为会生成CMakeLists.txt文件,不用自己一个一个写,当然后期形成模板,完全可以脱离CubeMX
2.CMakeLists.txt 修改(CMake部分提前说明如何修改,后面移植就不提了)
伴随着工程文件添加,需要在CMakeLists.txt 中添加需要编译的文件信息,类似于Keil IAR工作空间中添加文件。
实际添加起来会比以往的IDE便捷很多
3.FreeRTOS移植
FreeRTOS - Free RTOS Source Code Downloads, the official FreeRTOS zip file release download
这里笔者使用的是V10.3.0版本,修改FreeRTOSConfig.h文件和SysTick_Handler函数
//根据需要确定系统堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(16 * 1024))
//lvgl需要使用,这里打开
#define configUSE_TICK_HOOK 1
//屏蔽 xPortSysTickHandler
//#define xPortSysTickHandler SysTick_Handler
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
extern void xPortSysTickHandler(void);
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
4.lvgl移植
LVGL - Light and Versatile Embedded Graphics Library
mirrors / lvgl / lvgl · GitCode
复制一份lv_conf_template.h 命名为 lv_conf.h,开启宏
因为使用的开发板屏幕分辨率是:240*240 若做整个屏幕16bit刷新,那需要的缓存是:115200字节,显然在RAM只有128K的单片机上使用是不现实的,针对RAM较小的单片机只能做部分缓冲,如下操作:
/*******************************************************************************
* Definitions
******************************************************************************/
#define LCD_WIDTH 240
#define LCD_HEIGHT 240
#define LCD_FB_BYTE_PER_PIXEL 2
/* The virtual buffer for DBI panel, it should be ~1/10 screen size. */
#define LCD_VIRTUAL_BUF_SIZE (LCD_WIDTH * LCD_HEIGHT / 10)
/*******************************************************************************
* Variables
******************************************************************************/
static uint8_t s_frameBuffer[2][LCD_VIRTUAL_BUF_SIZE * LCD_FB_BYTE_PER_PIXEL];
void lv_port_disp_init(void)
{
static lv_disp_draw_buf_t disp_buf;
lv_disp_draw_buf_init(&disp_buf, s_frameBuffer[0], NULL, LCD_VIRTUAL_BUF_SIZE); //s_frameBuffer[1]
/*-------------------------
* Initialize your display
* -----------------------*/
xSYS_ST7789_Init();
/*-----------------------------------
* Register the display in LittlevGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set the resolution of the display*/
disp_drv.hor_res = LCD_WIDTH;
disp_drv.ver_res = LCD_HEIGHT;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = DEMO_FlushDisplay;
/*Set a display buffer*/
disp_drv.draw_buf = &disp_buf;
/* Partial refresh */
disp_drv.full_refresh = 1;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
static void DEMO_FlushDisplay(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
lv_coord_t x1 = area->x1;
lv_coord_t y1 = area->y1;
lv_coord_t x2 = area->x2;
lv_coord_t y2 = area->y2;
uint32_t size = (x2 - x1 + 1) * (y2 - y1 + 1) * 2;
//LCD_Fill(x1,y1,x2,y2,color_p);
LCD_Address_Set(x1,y1,x2,y2);
xSYS_LCD_Fill((uint8_t*)color_p,size);
lv_disp_flush_ready(disp_drv);
}
除此之外添加如下代码:
void vApplicationTickHook(void)
{
if (s_lvgl_initialized)
{
lv_tick_inc(1);
}
}
void xGUI_MainTask(void *pvParameters)
{
lv_port_pre_init();
lv_init();
lv_port_disp_init(); //显示初始化
s_lvgl_initialized = true;
pMainWidget = lv_scr_act(); //主体 Widget 创建
lv_timer_create(lv_timer_user_callback,500,NULL);
while(1)
{
lv_task_handler(); //lvgl 任务处理函数
vTaskDelay(5);
}
}
static void lv_timer_user_callback(lv_timer_t * timer)
{
static uint32_t colorCnt;
lv_obj_set_style_bg_color(pMainWidget, lv_palette_main((lv_palette_t)colorCnt),LV_PART_MAIN);
colorCnt++;
colorCnt = colorCnt%12;
}
5.其他注意事项
每次添加了文件,需要刷新CMake再执行编译,否则会移植报错(原因是CMake Cache需要刷新)