文章目录

  • 小熊派LiteOS移植LVGL
    • 一、移植前言
    • 二、配置 TFT
    • 三、LVGL 源码获取
    • 四、显示接口移植
    • 五、Demo 代码
    • 六、实验现象

小熊派LiteOS移植LVGL

一、移植前言

之前使用小熊派实现了鸿蒙动画的开机界面,具体使用的技术栈为 STM32 + LiteOS + LVGL + FATFS +DMA 方式实现,刷新效率非常高,预览视频如下:

关于这个的实现过程我会写一系列的教程分享出来,主要分为下面几个部分,本节为第二部分,基于 LiteOS 移植 LVGL 显示接口

  • 小熊派移植华为 LiteOS-M(基于MDK):链接;
  • 小熊派基于 LiteOS 移植 LVGL 显示接口:链接;
  • 小熊派基于 LiteOS 移植 LVGL 文件系统:链接;
  • 小熊派实现鸿蒙开机界面(LiteOS+LVGL):链接;

本节的教程就是先通过 STM32CubeMX 来配置 小熊派的 TFT 初始化代码,开启 DMA 加速(不开启会卡出翔),配置完成后获取 LVGL 的代码,移植到工程里面,然后将 TFT 驱动接口和 LVGL 接口对接,在运行 Demo 代码

二、配置 TFT

我们在上一节移植好 LiteOS 工程的基础上使用 CubeMX 配置 TFT 的 SPI 接口,具体 SPI 驱动接口可以参考这篇文章:小熊派 FreeRTOS+SPI+DMA 驱动 TFT-LCD

SPI 配置完成如下:

开启 DMA,并且在 NVIC 里面使能中断

除了上面的 SPI 引脚还需要,配置 TFT 的其他控制引脚,关于引脚在参考文章中有写出来,配置完成如下:

在 MDK 工程根目录下创建 Hardware/LCD 文件夹用来存放驱动代码,驱动文件命名为 lcd.c 和 lcd.h

拷贝下面的代码进去

lcd.c

#include "lcd.h"
#include "gpio.h"
#include "spi.h"
#include "cmsis_os.h"extern osSemaphoreId_t DMA_SemaphoreHandle;/* USER CODE BEGIN 1 */
/*** @brief    SPI 发送字节函数* @param    TxData   要发送的数据* @param    size 发送数据的字节大小* @return  0:写入成功,其他:写入失败*/
uint8_t SPI_WriteByte(uint8_t *TxData,uint16_t size)
{osStatus_t result;//获取信号,如果上一个DMA传输完成//信号就能获取到,没有传输完成任务就挂起//等到传输完成再恢复result = osSemaphoreAcquire(DMA_SemaphoreHandle,0xFFFF);if(result == osOK){//获取成功return HAL_SPI_Transmit_DMA(&hspi2,TxData,size);}else{//获取失败return 1;}
}
//DMA 传输完成后会调用 SPI传输完成回调函数
//在该函数中我们释放信号
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{if(hspi->Instance == hspi2.Instance)osSemaphoreRelease(DMA_SemaphoreHandle);
}/*** @brief   写命令到LCD* @param   cmd —— 需要发送的命令* @return  none*/
static void LCD_Write_Cmd(uint8_t cmd)
{LCD_WR_RS(0);SPI_WriteByte(&cmd, 1);
}/*** @brief   写数据到LCD* @param   dat —— 需要发送的数据* @return  none*/
static void LCD_Write_Data(uint8_t dat)
{LCD_WR_RS(1);SPI_WriteByte(&dat, 1);
}/*** @breif   打开LCD显示背光* @param   none* @return  none*/
void LCD_DisplayOn(void)
{LCD_PWR(1);
}
/*** @brief   关闭LCD显示背光* @param   none* @return  none*/
void LCD_DisplayOff(void)
{LCD_PWR(0);
}/*** @brief   设置数据写入LCD显存区域* @param   x1,y1  —— 起点坐标* @param   x2,y2    —— 终点坐标* @return  none*/
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{/* 指定X方向操作区域 */LCD_Write_Cmd(0x2a);LCD_Write_Data(x1 >> 8);LCD_Write_Data(x1);LCD_Write_Data(x2 >> 8);LCD_Write_Data(x2);/* 指定Y方向操作区域 */LCD_Write_Cmd(0x2b);LCD_Write_Data(y1 >> 8);LCD_Write_Data(y1);LCD_Write_Data(y2 >> 8);LCD_Write_Data(y2);/* 发送该命令,LCD开始等待接收显存数据 */LCD_Write_Cmd(0x2C);
}/*** @brief   以一种颜色清空LCD屏* @param   color —— 清屏颜色(16bit)* @return  none*/
void LCD_Clear(uint16_t color)
{uint16_t i;uint8_t data[2] = {0};  //color是16bit的,每个像素点需要两个字节的显存/* 将16bit的color值分开为两个单独的字节 */data[0] = color >> 8;data[1] = color;LCD_Address_Set(0, 0, LCD_Width - 1, LCD_Height - 1);LCD_WR_RS(1);for(i=0;i<((LCD_Width)*(LCD_Height));i++){SPI_WriteByte(data, 2);}
}/*** @brief   LCD初始化* @param   none* @return  none*/
void LCD_Init(void)
{/* 复位LCD */LCD_PWR(0);LCD_RST(0);osDelay(100);LCD_RST(1);osDelay(120);/* 关闭睡眠模式 */LCD_Write_Cmd(0x11);osDelay(120);/* 开始设置显存扫描模式,数据格式等 */LCD_Write_Cmd(0x36);LCD_Write_Data(0x00);/* RGB 5-6-5-bit格式  */LCD_Write_Cmd(0x3A);LCD_Write_Data(0x65);/* porch 设置 */LCD_Write_Cmd(0xB2);LCD_Write_Data(0x0C);LCD_Write_Data(0x0C);LCD_Write_Data(0x00);LCD_Write_Data(0x33);LCD_Write_Data(0x33);/* VGH设置 */LCD_Write_Cmd(0xB7);LCD_Write_Data(0x72);/* VCOM 设置 */LCD_Write_Cmd(0xBB);LCD_Write_Data(0x3D);/* LCM 设置 */LCD_Write_Cmd(0xC0);LCD_Write_Data(0x2C);/* VDV and VRH 设置 */LCD_Write_Cmd(0xC2);LCD_Write_Data(0x01);/* VRH 设置 */LCD_Write_Cmd(0xC3);LCD_Write_Data(0x19);/* VDV 设置 */LCD_Write_Cmd(0xC4);LCD_Write_Data(0x20);/* 普通模式下显存速率设置 60Mhz */LCD_Write_Cmd(0xC6);LCD_Write_Data(0x0F);/* 电源控制 */LCD_Write_Cmd(0xD0);LCD_Write_Data(0xA4);LCD_Write_Data(0xA1);/* 电压设置 */LCD_Write_Cmd(0xE0);LCD_Write_Data(0xD0);LCD_Write_Data(0x04);LCD_Write_Data(0x0D);LCD_Write_Data(0x11);LCD_Write_Data(0x13);LCD_Write_Data(0x2B);LCD_Write_Data(0x3F);LCD_Write_Data(0x54);LCD_Write_Data(0x4C);LCD_Write_Data(0x18);LCD_Write_Data(0x0D);LCD_Write_Data(0x0B);LCD_Write_Data(0x1F);LCD_Write_Data(0x23);/* 电压设置 */LCD_Write_Cmd(0xE1);LCD_Write_Data(0xD0);LCD_Write_Data(0x04);LCD_Write_Data(0x0C);LCD_Write_Data(0x11);LCD_Write_Data(0x13);LCD_Write_Data(0x2C);LCD_Write_Data(0x3F);LCD_Write_Data(0x44);LCD_Write_Data(0x51);LCD_Write_Data(0x2F);LCD_Write_Data(0x1F);LCD_Write_Data(0x1F);LCD_Write_Data(0x20);LCD_Write_Data(0x23);/* 显示开 */LCD_Write_Cmd(0x21);LCD_Write_Cmd(0x29);/*打开显示*/LCD_PWR(1);
}

lcd.h

#include "main.h"#define   LCD_PWR(n)      (n?\HAL_GPIO_WritePin(LCD_PWR_GPIO_Port,LCD_PWR_Pin,GPIO_PIN_SET):\HAL_GPIO_WritePin(LCD_PWR_GPIO_Port,LCD_PWR_Pin,GPIO_PIN_RESET))
#define LCD_WR_RS(n)    (n?\HAL_GPIO_WritePin(LCD_WR_RS_GPIO_Port,LCD_WR_RS_Pin,GPIO_PIN_SET):\HAL_GPIO_WritePin(LCD_WR_RS_GPIO_Port,LCD_WR_RS_Pin,GPIO_PIN_RESET))
#define LCD_RST(n)      (n?\HAL_GPIO_WritePin(LCD_RST_GPIO_Port,LCD_RST_Pin,GPIO_PIN_SET):\HAL_GPIO_WritePin(LCD_RST_GPIO_Port,LCD_RST_Pin,GPIO_PIN_RESET))//LCD屏幕分辨率定义
#define LCD_Width   240
#define LCD_Height  240
//颜色定义
#define WHITE   0xFFFF  //白色
#define YELLOW  0xFFE0  //黄色
#define BRRED   0XFC07  //棕红色
#define PINK    0XF81F  //粉色
#define RED     0xF800  //红色
#define BROWN   0XBC40  //棕色
#define GRAY    0X8430  //灰色
#define GBLUE   0X07FF  //兰色
#define GREEN   0x07E0  //绿色
#define BLUE    0x001F  //蓝色
#define BLACK   0x0000  //黑色uint8_t SPI_WriteByte(uint8_t *TxData,uint16_t size);
static void LCD_Write_Cmd(uint8_t cmd);
static void LCD_Write_Data(uint8_t dat);
void LCD_DisplayOn(void);
void LCD_DisplayOff(void);
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void LCD_Clear(uint16_t color);
void LCD_Init(void);

代码和文件添加完成后不要忘记添加文件路径,然后我们在主函数中创建一个用于 lcd 显示的任务,初始化 LCD 同时将屏幕初始化为蓝色

osThreadId_t lcd_taskHandle;
const osThreadAttr_t lcd_task_attributes = {.name = "lcd_task",.stack_size = 512 * 4,.priority = (osPriority_t) osPriorityNormal1,
};
void Lcd_Task(void *argument);
void Lcd_Task(void *argument)
{LCD_Init();LCD_Clear(BLUE);while(1){osDelay(1000);}
}

添加 DMA 信号量

osSemaphoreId_t DMA_SemaphoreHandle;
const osSemaphoreAttr_t DMA_Semaphore_attributes = {.name = "DMA_Semaphore"
};

初始化信号和 LiteOS:

  /* USER CODE BEGIN 2 */osKernelInitialize();/* creation of uart_task */DMA_SemaphoreHandle = osSemaphoreNew(1, 1, &DMA_Semaphore_attributes);led_taskHandle = osThreadNew(Led_Task, NULL, &led_task_attributes);lcd_taskHandle = osThreadNew(Lcd_Task, NULL, &lcd_task_attributes);osKernelStart();/* USER CODE END 2 */

编译烧写程序,观察现象,屏幕清屏为蓝色,驱动程序跑通了,可以进行下一步:

三、LVGL 源码获取

获取 lvgl 7.0 版本的源码

git clone -b release/v7 https://github.com/lvgl/lvgl.git

拉取后代码

下面我们在 MDK 工程目录按照下面的格式建立文件夹

APP 文件夹用来存放我们编写的 lvgl 应用代码,LVGL 文件夹用来存放 lvgl 的源码,以及接口代码

然后我们将刚刚 github 下载的源码拷贝到 LVGL 中,然后把里面 lvgl\examples\porting 文件夹复制到同一目录下,改名为 lvgl_port 文件夹,同时将 lvgl\lv_conf_template.h 也复制到同一目录,并且改名为 lv_conf.h,修改结果如下:

然后将 lvgl_port 下面的文件也修改名称为下面的格式:

这6个文件是 lvgl 的接口文件,disp 是显示接口、fs 是文件系统接口、indev 是输入接口,下面我们在 MDK 工程里面添加文件和文件路径,添加路径如下:

..\Middlewares\LVGL\APP
..\Middlewares\LVGL\LVGL\lvgl_port
..\Middlewares\LVGL\LVGL\lvgl\src

添加文件如下:

src 放的文件是下面文件夹的所有 c 文件

config 放的是 lvgl 配置头文件:

port 放的是 lvgl 的硬件接口文件

文件添加完成后我们先配置 lvgl 下的 lv_conf.h 文件,做一些配置,不然直接编译的话会有一堆报错

lv_conf.h 文件修改:

修改屏幕尺寸适配小熊派:

/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX          (240)
#define LV_VER_RES_MAX          (240)

设置屏幕颜色深度,以及颜色存放格式(适配 ST7789芯片):

/* Color depth:* - 1:  1 byte per pixel* - 8:  RGB332* - 16: RGB565* - 32: ARGB8888*/
#define LV_COLOR_DEPTH     16
/* Swap the 2 bytes of RGB565 color.* Useful if the display has a 8 bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP   1

设置调节界面缩放比例:

/* Dot Per Inch: used to initialize default sizes.* E.g. a button with width = LV_DPI / 2 -> half inch wide* (Not so important, you can adjust it to modify default sizes and spaces)*/
#define LV_DPI              60     /*[px]*/

设置动态内存大小:

/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
#  define LV_MEM_SIZE    (16U * 1024U)

关闭使用 GPU:

/* 1: Enable GPU interface*/
#define LV_USE_GPU              0   /*Only enables `gpu_fill_cb` and `gpu_blend_cb` in the disp. drv- */
#define LV_USE_GPU_STM32_DMA2D  0

暂时先关闭文件系统:

/* 1: Enable file system (might be required for images */
#define LV_USE_FILESYSTEM       0

编译一下,有一些警告

..\Middlewares\LVGL\LVGL\lvgl\src\lv_draw\lv_draw_mask.c(350): warning:  #111-D: statement is unreachable

这些警告没有任何影响,可以把警告给屏蔽掉,切换到 C/C++选项卡,在 Misc Controls 中填入

--diag_suppress=111 把它屏蔽掉如下图所示:

编译后改报错就不显示了

四、显示接口移植

编译通过后,我们下一步就是修改显示接口了,打开 lv_port_disp.c 文件,将开头使能,包括头文件也使能:

 /*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/
#if 1

修改显示接口,主要关注 void lv_port_disp_init(void) 函数

void lv_port_disp_init(void)
{/*-------------------------* Initialize your display* -----------------------*/disp_init();/*-----------------------------* Create a buffer for drawing*----------------------------*//* LVGL requires a buffer where it internally draws the widgets.* Later this buffer will passed your display drivers `flush_cb` to copy its content to your display.* The buffer has to be greater than 1 display row** There are three buffering configurations:* 1. Create ONE buffer with some rows:*      LVGL will draw the display's content here and writes it to your display** 2. Create TWO buffer with some rows:*      LVGL will draw the display's content to a buffer and writes it your display.*      You should use DMA to write the buffer's content to the display.*      It will enable LVGL to draw the next part of the screen to the other buffer while*      the data is being sent form the first buffer. It makes rendering and flushing parallel.** 3. Create TWO screen-sized buffer:*      Similar to 2) but the buffer have to be screen sized. When LVGL is ready it will give the*      whole frame to display. This way you only need to change the frame buffer's address instead of*      copying the pixels.* *//* Example for 1) */static lv_disp_buf_t draw_buf_dsc_1;static lv_color_t draw_buf_1[LV_HOR_RES_MAX * 10];                          /*A buffer for 10 rows*/lv_disp_buf_init(&draw_buf_dsc_1, draw_buf_1, NULL, LV_HOR_RES_MAX * 10);   /*Initialize the display buffer*//* Example for 2) */static lv_disp_buf_t draw_buf_dsc_2;static lv_color_t draw_buf_2_1[LV_HOR_RES_MAX * 10];                        /*A buffer for 10 rows*/static lv_color_t draw_buf_2_2[LV_HOR_RES_MAX * 10];                        /*An other buffer for 10 rows*/lv_disp_buf_init(&draw_buf_dsc_2, draw_buf_2_1, draw_buf_2_2, LV_HOR_RES_MAX * 10);   /*Initialize the display buffer*//* Example for 3) */static lv_disp_buf_t draw_buf_dsc_3;static lv_color_t draw_buf_3_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];            /*A screen sized buffer*/static lv_color_t draw_buf_3_2[LV_HOR_RES_MAX * LV_VER_RES_MAX];            /*An other screen sized buffer*/lv_disp_buf_init(&draw_buf_dsc_3, draw_buf_3_1, draw_buf_3_2, LV_HOR_RES_MAX * LV_VER_RES_MAX);   /*Initialize the display buffer*//*-----------------------------------* Register the display in LVGL*----------------------------------*/lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/lv_disp_drv_init(&disp_drv);                    /*Basic initialization*//*Set up the functions to access to your display*//*Set the resolution of the display*/disp_drv.hor_res = 480;disp_drv.ver_res = 320;/*Used to copy the buffer's content to the display*/disp_drv.flush_cb = disp_flush;/*Set a display buffer*/disp_drv.buffer = &draw_buf_dsc_1;#if LV_USE_GPU/*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*//*Blend two color array using opacity*/disp_drv.gpu_blend_cb = gpu_blend;/*Fill a memory array with a color*/disp_drv.gpu_fill_cb = gpu_fill;
#endif/*Finally register the driver*/lv_disp_drv_register(&disp_drv);
}

disp_init() 用来初始化显示屏外设,这里我们在hal初始化中已经初始化完成了,所以删除他

下面的代码就是创建一个缓存 buffer,这里 LVGL 提供了三种方式创建缓存:

第一种只创建一个缓存区,长度是横轴像素长度的 10 倍,第二种创建两个缓存区,长度都是 横轴的 10 倍,第三种则是创建两个,大小是横轴乘以纵轴,相当于整个屏幕大小,第一种情况,如果我们在写入数据时不能修改,第二种我们在写入一个 buffer 时还可以希尔另外一个 buffer ,可以结合 DMA 加快写入速度,这里我使用第一种

下面的代码注册显示驱动,配置其参数:

主要就是配置屏幕参数,设置刷新函数,配置缓存区指针,最后注册驱动,这里我们要修改一下刷新屏幕函数

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)

修改如下:

/* Flush the content of the internal buffer the specific area on the display* You can use DMA or any hardware acceleration to do this operation in the background but* 'lv_disp_flush_ready()' has to be called when finished. */
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/int32_t y;LCD_Address_Set(area->x1,area->y1,area->x2,area->y2);LCD_WR_RS(1);//一行一行 DMAfor(y = area->y1; y <= area->y2; y++) {if(osSemaphoreAcquire(DMA_SemaphoreHandle,0xFFFF) == osOK)HAL_SPI_Transmit_DMA(&hspi2,(uint8_t *)color_p,(uint16_t)(area->x2-area->x1+1)*2);color_p += (area->x2-area->x1+1);}/* IMPORTANT!!!* Inform the graphics library that you are ready with the flushing*/lv_disp_flush_ready(disp_drv);
}

修改代码后,要添加头文件和 dma 信号量声明

/**********************      INCLUDES*********************/
#include "lv_port_disp.h"
#include "lcd.h"
#include "spi.h"
#include "cmsis_os.h"
/**********************      DEFINES*********************/
extern osSemaphoreId_t DMA_SemaphoreHandle;

五、Demo 代码

在定时器 1 中断中添加 lvgl 的时基更新代码

void TIM1_UP_TIM16_IRQHandler(void)
{/* USER CODE BEGIN TIM1_UP_TIM16_IRQn 0 *//* USER CODE END TIM1_UP_TIM16_IRQn 0 */HAL_TIM_IRQHandler(&htim1);/* USER CODE BEGIN TIM1_UP_TIM16_IRQn 1 */lv_tick_inc(1);/* USER CODE END TIM1_UP_TIM16_IRQn 1 */
}

在 main.c 的 lcd 任务中添加创建 label 测试的代码

这里的测试代码是画两个对角位置的方块,边框颜色都不一样,一个设置的是蓝色,一个是绿色

void Lcd_Task(void *argument)
{LCD_Init();lv_init();lv_port_disp_init();//lvgl 显示接口初始化,放在 lv_init()的后面lv_style_t style1;lv_style_init(&style1);lv_style_set_bg_color(&style1, LV_STATE_DEFAULT,LV_COLOR_BLACK);lv_style_set_border_width(&style1,LV_STATE_DEFAULT, 5);lv_style_set_border_color(&style1,LV_STATE_DEFAULT, LV_COLOR_BLUE);lv_style_t style2;lv_style_init(&style2);lv_style_set_bg_color(&style2, LV_STATE_DEFAULT,LV_COLOR_BLACK);lv_style_set_border_width(&style2,LV_STATE_DEFAULT, 5);lv_style_set_border_color(&style2,LV_STATE_DEFAULT, LV_COLOR_GREEN);lv_obj_t* bgk1 = lv_obj_create(lv_scr_act(), NULL);//创建对象lv_obj_set_pos(bgk1,0,0);lv_obj_set_size(bgk1, 120, 120);//设置覆盖大小lv_obj_add_style(bgk1,LV_STATE_DEFAULT, &style1);lv_obj_t* bgk2 = lv_obj_create(lv_scr_act(), NULL);//创建对象lv_obj_set_pos(bgk2,120,120);lv_obj_set_size(bgk2, 120, 120);//设置覆盖大小  lv_obj_add_style(bgk2,LV_STATE_DEFAULT, &style2);while(1){lv_task_handler();osDelay(1000);}
}
/* USER CODE END 0 */

记得添加相关头文件:

/* USER CODE BEGIN Includes */
#include "cmsis_os.h"
#include "lcd.h"
#include "lv_port_disp.h"
/* USER CODE END Includes */

编译下载代码

六、实验现象

两个对角小方块,边框一个蓝色一个绿色

小熊派LiteOS移植LVGL相关推荐

  1. 小熊派移植华为 LiteOS-M(基于MDK)

    文章目录 小熊派移植华为 LiteOS-M(基于MDK) 前言 一.软硬件介绍 1.1 小熊派开发板 1.2 LiteOS介绍 1.3 移植前言 二.CubeMX 配置 三.获取源码 四.源码移植 4 ...

  2. 手把手教你丨小熊派移植华为 LiteOS-M

    摘要:本文详细讲解如何移植 LiteOS 到小熊派. 本文分享自华为云社区<小熊派移植华为 LiteOS-M(基于MDK)>,作者: JeckXu666. 前言 之前使用小熊派实现了鸿蒙动 ...

  3. 小熊派开发实践丨漫谈LiteOS之传感器移植

    摘要:本文基于小熊派开发板简单介绍了如何在LiteOS中移植传感器,从而实现对于传感器的相关控制. 1 hello world 相信大家无论在学习编程语言开始的第一个函数应该是HelloWorld,本 ...

  4. 【华为云技术分享】小熊派华为物联网操作系统LiteOS裸机驱动移植02-LCD驱动移植及使用

    1. LCD裸机驱动 小熊派开发板使用的LCD屏幕为1.3寸的TFT彩屏,色彩深度16bit,分辨率240*240,使用 SPI 接口与 MCU 之间通信. 如果你对裸机玩转LCD屏幕还不熟悉,请先阅 ...

  5. 【华为云技术分享】小熊派IoT开发板华为物联网操作系统LiteOS内核实战教程01-IoT-Studio介绍及安装

    1. 物联网一站式开发工具 -- IoT Studio IoT Studio 是支持 LiteOS 嵌入式系统软件开发的工具,提供了代码编辑.编译.烧录 及调试等一站式开发体验,支持 C.C++.汇编 ...

  6. 小熊派 移植TencentOS-tiny+EC20+MQTT对接腾讯云

    1.参考文档 TencentOS tiny端云对接开发指南(IoTHub)以及Tencent tiny源码库 TencentOS-tiny官网 2.云端配置 登陆腾讯云,点击"云产品&quo ...

  7. 小熊派开发板移植RT-FOTA

    前言 买了小熊派的开发板,将demo code验证完成之后就放下了.刚好最近工作也在做OTA相关的开发,发现自己对于升级的功能还不够了解,在码云找到了一位大神基于RTThread的RT-FOTA代码, ...

  8. 小熊派bearpi-HM-nano(hi3861鸿蒙LiteOS)点亮WS2812灯板(spi方式)

    官网代码仓:BearPi-HM_Nano: 小熊派BearPi-HM Nano开发板基于HarmonyOS的源码 代码仓和全网几乎没有hi3861SPI相关的例程序,故分享给大家(首发哈哈) 这个是代 ...

  9. stm32mp157系统移植 | 移植ST官方5.10内核到小熊派开发板

    Linux内核移植流程 移植Linux内核到一个新的板子上时,需要以下步骤: (1)选择一个硬件设计接近的板子,基于此板相关文件开始移植 (2)拷贝单板配置文件(arch/arm/configs) ( ...

  10. 实战贴:GUI STemWin在小熊派上的移植(显示很漂亮、很细腻,棒!)

    开篇前来一首歌放松一下,推荐简弘亦的新歌:忽如一面,妥妥的歌唱实力派,可是不知道为啥感觉一直不温不火,我是听了他的不染喜欢上的,好了,进入正题. 现在市面上有很多成熟的GUI,老早前就听说过STemW ...

最新文章

  1. AI破解脑电波,准确率超80%!高度还原你眼中最美的ta
  2. 词袋模型BoW图像检索Python实战
  3. Java 面试经典题解析:谈谈你对 Java 平台的理解?
  4. Hibernate问题浅析
  5. mysql抽屉图标_React Native自定义组件实现抽屉菜单控件效果
  6. Elasticsearch一些常用操作和一些基础概念
  7. Linux下命令积累
  8. 刘逖:ETF市场迎来发展时机 可从产品创新等方面推动
  9. 小型网站项目完整部署流程(Windows操作系统)
  10. 什么是PHP,如何快速运行PHP文件
  11. Python实现快速大文件比较代码解析
  12. Labwindows/CVI 编写CAN通讯的上位机
  13. unity 角度限制_Unity3D实现摄像机镜头移动并限制角度
  14. C++-灰度图上色GrayToColor
  15. kubeadm搭建k8s集群
  16. 变异凯撒(实验吧CTF题库-密码学)
  17. 任泽平最新演讲:从这6个周期,读懂中国经济未来走势
  18. Android车机初体验
  19. 安卓手机去水印哪个好用
  20. matlab在频率特性法中的应用实验目的,matlab软件实习报告

热门文章

  1. 等额本金等额本息计算器
  2. 五人合伙最佳股份分配_五人合伙股份分配协议书范本
  3. access横向求和sum_access 2007使用查询对数据求和
  4. 可任意改变背景颜色的万年历
  5. EXCEL条件格式小知识:条件判断如何写公式,可多层if
  6. JavaScript中throw的错误异常处理
  7. python - 1024 Spider
  8. php 调用 百度句法分析,【原创源码】百度新版翻译API调用使用范例
  9. 跨平台的会员通 打通品牌任督二脉
  10. android光谱图软件,光谱精灵精华版Plus