加入底盘,屏幕能亮了

This commit is contained in:
shentou 2025-12-20 15:29:56 +08:00
parent e4908c41b3
commit a708f48a59
30 changed files with 1208 additions and 158 deletions

View File

@ -55,6 +55,10 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
./User/bsp/time.c ./User/bsp/time.c
./User/component/user_math.c ./User/component/user_math.c
./User/module/config.c ./User/module/config.c
./User/task/init.c
./User/task/display.c
./User/task/user_task.c
./User/LCD/lcd.c ./User/LCD/lcd.c
@ -64,6 +68,7 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
./User/LCD/touch.c ./User/LCD/touch.c
./User/lvgl/examples/porting/lv_port_disp_template.c ./User/lvgl/examples/porting/lv_port_disp_template.c
./User/lvgl/examples/porting/lv_port_indev_template.c ./User/lvgl/examples/porting/lv_port_indev_template.c
./User/lvgl/stress/lv_demo_stress.c
${LVGL_C} ${LVGL_C}

View File

@ -96,7 +96,7 @@
* (when HSE is used as system clock source, directly or through the PLL). * (when HSE is used as system clock source, directly or through the PLL).
*/ */
#if !defined (HSE_VALUE) #if !defined (HSE_VALUE)
#define HSE_VALUE 25000000U /*!< Value of the External oscillator in Hz */ #define HSE_VALUE 8000000U /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */ #endif /* HSE_VALUE */
#if !defined (HSE_STARTUP_TIMEOUT) #if !defined (HSE_STARTUP_TIMEOUT)

View File

@ -56,6 +56,7 @@ void CAN1_RX0_IRQHandler(void);
void CAN1_RX1_IRQHandler(void); void CAN1_RX1_IRQHandler(void);
void USART1_IRQHandler(void); void USART1_IRQHandler(void);
void TIM8_TRG_COM_TIM14_IRQHandler(void); void TIM8_TRG_COM_TIM14_IRQHandler(void);
void DMA2_Stream0_IRQHandler(void);
/* USER CODE BEGIN EFP */ /* USER CODE BEGIN EFP */
/* USER CODE END EFP */ /* USER CODE END EFP */

View File

@ -64,6 +64,11 @@ void MX_DMA_Init(void)
Error_Handler(); Error_Handler();
} }
/* DMA interrupt init */
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
} }
/* USER CODE BEGIN 2 */ /* USER CODE BEGIN 2 */

View File

@ -26,6 +26,7 @@
/* Private includes ----------------------------------------------------------*/ /* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN Includes */
#include "task/user_task.h"
/* USER CODE END Includes */ /* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/
@ -96,6 +97,7 @@ void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN RTOS_THREADS */ /* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */ /* add threads, ... */
osThreadNew(Task_Init, NULL, &attr_init); // 创建初始化任务
/* USER CODE END RTOS_THREADS */ /* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */ /* USER CODE BEGIN RTOS_EVENTS */
@ -114,11 +116,7 @@ void MX_FREERTOS_Init(void) {
void StartDefaultTask(void *argument) void StartDefaultTask(void *argument)
{ {
/* USER CODE BEGIN StartDefaultTask */ /* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */ osThreadTerminate(osThreadGetId());
for(;;)
{
osDelay(1);
}
/* USER CODE END StartDefaultTask */ /* USER CODE END StartDefaultTask */
} }

View File

@ -42,12 +42,25 @@
void MX_GPIO_Init(void) void MX_GPIO_Init(void)
{ {
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */ /* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
/*Configure GPIO pin : PB0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
} }
/* USER CODE BEGIN 2 */ /* USER CODE BEGIN 2 */

View File

@ -141,8 +141,8 @@ void SystemClock_Config(void)
RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25; RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4; RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

View File

@ -60,6 +60,7 @@ void SysTick_Port (void);
/* External variables --------------------------------------------------------*/ /* External variables --------------------------------------------------------*/
extern CAN_HandleTypeDef hcan1; extern CAN_HandleTypeDef hcan1;
extern DMA_HandleTypeDef hdma_memtomem_dma2_stream0;
extern UART_HandleTypeDef huart1; extern UART_HandleTypeDef huart1;
extern TIM_HandleTypeDef htim14; extern TIM_HandleTypeDef htim14;
@ -221,6 +222,20 @@ void TIM8_TRG_COM_TIM14_IRQHandler(void)
/* USER CODE END TIM8_TRG_COM_TIM14_IRQn 1 */ /* USER CODE END TIM8_TRG_COM_TIM14_IRQn 1 */
} }
/**
* @brief This function handles DMA2 stream0 global interrupt.
*/
void DMA2_Stream0_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream0_IRQn 0 */
/* USER CODE END DMA2_Stream0_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_memtomem_dma2_stream0);
/* USER CODE BEGIN DMA2_Stream0_IRQn 1 */
/* USER CODE END DMA2_Stream0_IRQn 1 */
}
/* USER CODE BEGIN 1 */ /* USER CODE BEGIN 1 */
/* USER CODE END 1 */ /* USER CODE END 1 */

View File

@ -604,68 +604,77 @@ void lcd_set_window(uint16_t sx, uint16_t sy, uint16_t width, uint16_t height)
* @param * @param
* @retval * @retval
*/ */
void lcd_init(void)
__weak void lcd_init(void)
{ {
GPIO_InitTypeDef gpio_init_struct;
FSMC_NORSRAM_TimingTypeDef fsmc_read_handle;
FSMC_NORSRAM_TimingTypeDef fsmc_write_handle;
LCD_CS_GPIO_CLK_ENABLE(); /* LCD_CS脚时钟使能 */ // lcddev.id =0x7789;
LCD_WR_GPIO_CLK_ENABLE(); /* LCD_WR脚时钟使能 */ // lcd_ex_st7789_reginit();
LCD_RD_GPIO_CLK_ENABLE(); /* LCD_RD脚时钟使能 */ // lcd_display_dir(1);
LCD_RS_GPIO_CLK_ENABLE(); /* LCD_RS脚时钟使能 */ // LCD_BL(1);
LCD_BL_GPIO_CLK_ENABLE(); /* LCD_BL脚时钟使能 */ // lcd_clear(WHITE);
// return;
gpio_init_struct.Pin = LCD_CS_GPIO_PIN; // GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 推挽复用 */ // FSMC_NORSRAM_TimingTypeDef fsmc_read_handle;
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ // FSMC_NORSRAM_TimingTypeDef fsmc_write_handle;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ //
gpio_init_struct.Alternate = GPIO_AF12_FSMC; /* 复用为FSMC */ // LCD_CS_GPIO_CLK_ENABLE(); /* LCD_CS脚时钟使能 */
HAL_GPIO_Init(LCD_CS_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_CS引脚 */ // LCD_WR_GPIO_CLK_ENABLE(); /* LCD_WR脚时钟使能 */
// LCD_RD_GPIO_CLK_ENABLE(); /* LCD_RD脚时钟使能 */
gpio_init_struct.Pin = LCD_WR_GPIO_PIN; // LCD_RS_GPIO_CLK_ENABLE(); /* LCD_RS脚时钟使能 */
HAL_GPIO_Init(LCD_WR_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_WR引脚 */ // LCD_BL_GPIO_CLK_ENABLE(); /* LCD_BL脚时钟使能 */
//
gpio_init_struct.Pin = LCD_RD_GPIO_PIN; // gpio_init_struct.Pin = LCD_CS_GPIO_PIN;
HAL_GPIO_Init(LCD_RD_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_RD引脚 */ // gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 推挽复用 */
// gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Pin = LCD_RS_GPIO_PIN; // gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(LCD_RS_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_RS引脚 */ // gpio_init_struct.Alternate = GPIO_AF12_FSMC; /* 复用为FSMC */
// HAL_GPIO_Init(LCD_CS_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_CS引脚 */
gpio_init_struct.Pin = LCD_BL_GPIO_PIN; //
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */ // gpio_init_struct.Pin = LCD_WR_GPIO_PIN;
HAL_GPIO_Init(LCD_BL_GPIO_PORT, &gpio_init_struct); /* LCD_BL引脚模式设置(推挽输出) */ // HAL_GPIO_Init(LCD_WR_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_WR引脚 */
//
g_sram_handle.Instance = FSMC_NORSRAM_DEVICE; // gpio_init_struct.Pin = LCD_RD_GPIO_PIN;
g_sram_handle.Extended = FSMC_NORSRAM_EXTENDED_DEVICE; // HAL_GPIO_Init(LCD_RD_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_RD引脚 */
//
g_sram_handle.Init.NSBank = FSMC_NORSRAM_BANK4; /* 使用NE4 */ // gpio_init_struct.Pin = LCD_RS_GPIO_PIN;
g_sram_handle.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE; /* 地址/数据线不复用 */ // HAL_GPIO_Init(LCD_RS_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_RS引脚 */
g_sram_handle.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16; /* 16位数据宽度 */ //
g_sram_handle.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE; /* 是否使能突发访问,仅对同步突发存储器有效,此处未用到 */ // gpio_init_struct.Pin = LCD_BL_GPIO_PIN;
g_sram_handle.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; /* 等待信号的极性,仅在突发模式访问下有用 */ // gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
g_sram_handle.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS; /* 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT */ // HAL_GPIO_Init(LCD_BL_GPIO_PORT, &gpio_init_struct); /* LCD_BL引脚模式设置(推挽输出) */
g_sram_handle.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE; /* 存储器写使能 */ //
g_sram_handle.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE; /* 等待使能位,此处未用到 */ // g_sram_handle.Instance = FSMC_NORSRAM_DEVICE;
g_sram_handle.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE; /* 读写使用不同的时序 */ // g_sram_handle.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
g_sram_handle.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE; /* 是否使能同步传输模式下的等待信号,此处未用到 */ //
g_sram_handle.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE; /* 禁止突发写 */ // g_sram_handle.Init.NSBank = FSMC_NORSRAM_BANK4; /* 使用NE4 */
// g_sram_handle.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE; /* 地址/数据线不复用 */
/* FSMC读时序控制寄存器 */ // g_sram_handle.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16; /* 16位数据宽度 */
fsmc_read_handle.AddressSetupTime = 0x0F; /* 地址建立时间(ADDSET)为15个fsmc_ker_ck(1/168=6)即6*15=90ns */ // g_sram_handle.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE; /* 是否使能突发访问,仅对同步突发存储器有效,此处未用到 */
fsmc_read_handle.AddressHoldTime = 0x00; /* 地址保持时间(ADDHLD) 模式A是没有用到 */ // g_sram_handle.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; /* 等待信号的极性,仅在突发模式访问下有用 */
fsmc_read_handle.DataSetupTime = 60; /* 数据保存时间(DATAST)为60个fsmc_ker_ck=6*60=360ns */ // g_sram_handle.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS; /* 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT */
/* 因为液晶驱动IC的读数据的时候,速度不能太快,尤其是个别奇葩芯片 */ // g_sram_handle.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE; /* 存储器写使能 */
fsmc_read_handle.AccessMode = FSMC_ACCESS_MODE_A; /* 模式A */ // g_sram_handle.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE; /* 等待使能位,此处未用到 */
// g_sram_handle.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE; /* 读写使用不同的时序 */
/* FSMC写时序控制寄存器 */ // g_sram_handle.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE; /* 是否使能同步传输模式下的等待信号,此处未用到 */
fsmc_write_handle.AddressSetupTime = 9; /* 地址建立时间(ADDSET)为9个fsmc_ker_ck=6*9=54ns */ // g_sram_handle.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE; /* 禁止突发写 */
fsmc_write_handle.AddressHoldTime = 0x00; /* 地址保持时间(ADDHLD) 模式A是没有用到 */ //
fsmc_write_handle.DataSetupTime = 9; /* 数据保存时间(DATAST)为9个fsmc_ker_ck=6*9=54ns */ // /* FSMC读时序控制寄存器 */
/* 注意某些液晶驱动IC的写信号脉宽最少也得50ns */ // fsmc_read_handle.AddressSetupTime = 0x0F; /* 地址建立时间(ADDSET)为15个fsmc_ker_ck(1/168=6)即6*15=90ns */
fsmc_write_handle.AccessMode = FSMC_ACCESS_MODE_A; /* 模式A */ // fsmc_read_handle.AddressHoldTime = 0x00; /* 地址保持时间(ADDHLD) 模式A是没有用到 */
// fsmc_read_handle.DataSetupTime = 60; /* 数据保存时间(DATAST)为60个fsmc_ker_ck=6*60=360ns */
HAL_SRAM_Init(&g_sram_handle, &fsmc_read_handle, &fsmc_write_handle); // /* 因为液晶驱动IC的读数据的时候,速度不能太快,尤其是个别奇葩芯片 */
// fsmc_read_handle.AccessMode = FSMC_ACCESS_MODE_A; /* 模式A */
//
// /* FSMC写时序控制寄存器 */
// fsmc_write_handle.AddressSetupTime = 9; /* 地址建立时间(ADDSET)为9个fsmc_ker_ck=6*9=54ns */
// fsmc_write_handle.AddressHoldTime = 0x00; /* 地址保持时间(ADDHLD) 模式A是没有用到 */
// fsmc_write_handle.DataSetupTime = 9; /* 数据保存时间(DATAST)为9个fsmc_ker_ck=6*9=54ns */
// /* 注意某些液晶驱动IC的写信号脉宽最少也得50ns */
// fsmc_write_handle.AccessMode = FSMC_ACCESS_MODE_A; /* 模式A */
//
// HAL_SRAM_Init(&g_sram_handle, &fsmc_read_handle, &fsmc_write_handle);
delay_ms(50); delay_ms(50);
/* 尝试9341 ID的读取 */ /* 尝试9341 ID的读取 */
@ -786,21 +795,21 @@ void lcd_init(void)
lcd_ssd_backlight_set(100); /* 背光设置为最亮 */ lcd_ssd_backlight_set(100); /* 背光设置为最亮 */
} }
/* 初始化完成以后,提速 */ // /* 初始化完成以后,提速 */
if (lcddev.id == 0x7789 || lcddev.id == 0x9341 || lcddev.id == 0x1963) /* 7789/9341/1963 提速 */ // if (lcddev.id == 0x7789 || lcddev.id == 0x9341 || lcddev.id == 0x1963) /* 7789/9341/1963 提速 */
{ // {
/* 重新配置写时序控制寄存器的时序 */ // /* 重新配置写时序控制寄存器的时序 */
fsmc_write_handle.AddressSetupTime = 3; /* 地址建立时间(ADDSET)为3个fsmc_ker_ck=6*3=18ns */ // fsmc_write_handle.AddressSetupTime = 3; /* 地址建立时间(ADDSET)为3个fsmc_ker_ck=6*3=18ns */
fsmc_write_handle.DataSetupTime = 3; /* 数据保持时间(DATAST)为3个fsmc_ker_ck=6*3=18ns */ // fsmc_write_handle.DataSetupTime = 3; /* 数据保持时间(DATAST)为3个fsmc_ker_ck=6*3=18ns */
FSMC_NORSRAM_Extended_Timing_Init(g_sram_handle.Extended, &fsmc_write_handle, g_sram_handle.Init.NSBank, g_sram_handle.Init.ExtendedMode); // FSMC_NORSRAM_Extended_Timing_Init(g_sram_handle.Extended, &fsmc_write_handle, g_sram_handle.Init.NSBank, g_sram_handle.Init.ExtendedMode);
} // }
else if (lcddev.id == 0x5310 || lcddev.id == 0x7796 || lcddev.id == 0x5510 || lcddev.id == 0x9806) /* 如果是这几个IC, 则设置WR时序为最快 */ // else if (lcddev.id == 0x5310 || lcddev.id == 0x7796 || lcddev.id == 0x5510 || lcddev.id == 0x9806) /* 如果是这几个IC, 则设置WR时序为最快 */
{ // {
/* 重新配置写时序控制寄存器的时序 */ // /* 重新配置写时序控制寄存器的时序 */
fsmc_write_handle.AddressSetupTime = 2; /* 地址建立时间(ADDSET)为2个fsmc_ker_ck=6*2=12ns */ // fsmc_write_handle.AddressSetupTime = 2; /* 地址建立时间(ADDSET)为2个fsmc_ker_ck=6*2=12ns */
fsmc_write_handle.DataSetupTime = 2; /* 数据保持时间(DATAST)为2个fsmc_ker_ck=6*2=12ns */ // fsmc_write_handle.DataSetupTime = 2; /* 数据保持时间(DATAST)为2个fsmc_ker_ck=6*2=12ns */
FSMC_NORSRAM_Extended_Timing_Init(g_sram_handle.Extended, &fsmc_write_handle, g_sram_handle.Init.NSBank, g_sram_handle.Init.ExtendedMode); // FSMC_NORSRAM_Extended_Timing_Init(g_sram_handle.Extended, &fsmc_write_handle, g_sram_handle.Init.NSBank, g_sram_handle.Init.ExtendedMode);
} // }
lcd_display_dir(0); /* 默认为竖屏 */ lcd_display_dir(0); /* 默认为竖屏 */
LCD_BL(1); /* 点亮背光 */ LCD_BL(1); /* 点亮背光 */

View File

@ -71,8 +71,8 @@
* LCD_FSMC_NEX, LCD_CS_GPIO相关设置也得改 * LCD_FSMC_NEX, LCD_CS_GPIO相关设置也得改
* LCD_FSMC_AX , LCD_RS_GPIO相关设置也得改 * LCD_FSMC_AX , LCD_RS_GPIO相关设置也得改
*/ */
#define LCD_FSMC_NEX 4 /* 使用FSMC_NE4接LCD_CS,取值范围只能是: 1~4 */ #define LCD_FSMC_NEX 1 /* 使用FSMC_NE4接LCD_CS,取值范围只能是: 1~4 */
#define LCD_FSMC_AX 6 /* 使用FSMC_A6接LCD_RS,取值范围是: 0 ~ 25 */ #define LCD_FSMC_AX 16 /* 使用FSMC_A6接LCD_RS,取值范围是: 0 ~ 25 */
#define LCD_FSMC_BCRX FSMC_Bank1->BTCR[(LCD_FSMC_NEX - 1) * 2] /* BCR寄存器,根据LCD_FSMC_NEX自动计算 */ #define LCD_FSMC_BCRX FSMC_Bank1->BTCR[(LCD_FSMC_NEX - 1) * 2] /* BCR寄存器,根据LCD_FSMC_NEX自动计算 */
#define LCD_FSMC_BTRX FSMC_Bank1->BTCR[(LCD_FSMC_NEX - 1) * 2 + 1] /* BTR寄存器,根据LCD_FSMC_NEX自动计算 */ #define LCD_FSMC_BTRX FSMC_Bank1->BTCR[(LCD_FSMC_NEX - 1) * 2 + 1] /* BTR寄存器,根据LCD_FSMC_NEX自动计算 */
@ -101,8 +101,8 @@ extern uint32_t g_back_color; /* 背景颜色.默认为白色 */
/* LCD背光控制 */ /* LCD背光控制 */
#define LCD_BL(x) do{ x ? \ #define LCD_BL(x) do{ x ? \
HAL_GPIO_WritePin(LCD_BL_GPIO_PORT, LCD_BL_GPIO_PIN, GPIO_PIN_SET) : \ HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LCD_BL_GPIO_PORT, LCD_BL_GPIO_PIN, GPIO_PIN_RESET); \ HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0, GPIO_PIN_RESET); \
}while(0) }while(0)
/* LCD地址结构体 */ /* LCD地址结构体 */

View File

@ -18,12 +18,12 @@
*********************/ *********************/
#ifndef MY_DISP_HOR_RES #ifndef MY_DISP_HOR_RES
#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now. #warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.
#define MY_DISP_HOR_RES 480 #define MY_DISP_HOR_RES 320
#endif #endif
#ifndef MY_DISP_VER_RES #ifndef MY_DISP_VER_RES
#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now. #warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now.
#define MY_DISP_VER_RES 320 #define MY_DISP_VER_RES 240
#endif #endif
@ -115,8 +115,8 @@ void lv_port_disp_init(void)
// /* Example for 2) */ // /* Example for 2) */
static lv_disp_draw_buf_t draw_buf_dsc_2; static lv_disp_draw_buf_t draw_buf_dsc_2;
static lv_color_t buf_2_1[MY_DISP_HOR_RES * 40]; /*A buffer for 10 rows*/ static lv_color_t buf_2_1[MY_DISP_HOR_RES * 20]; /*A buffer for 10 rows*/
static lv_color_t buf_2_2[MY_DISP_HOR_RES * 40]; /*An other buffer for 10 rows*/ static lv_color_t buf_2_2[MY_DISP_HOR_RES * 20]; /*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 * 40); /*Initialize the display buffer*/ lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 40); /*Initialize the display buffer*/
// /* Example for 3) also set disp_drv.full_refresh = 1 below*/ // /* Example for 3) also set disp_drv.full_refresh = 1 below*/

View File

@ -13,10 +13,10 @@ extern "C" {
/********************* /*********************
* INCLUDES * INCLUDES
*********************/ *********************/
#include "../lvgl.h" #include "lvgl.h"
#if LV_USE_DEMO_WIDGETS #if LV_USE_DEMO_WIDGETS
#include "widgets/lv_demo_widgets.h" #include "lvgl/examples/lv_examples.h"
#endif #endif
#if LV_USE_DEMO_BENCHMARK #if LV_USE_DEMO_BENCHMARK

View File

@ -0,0 +1,6 @@
- delay: 0
description: ''
freq_control: false
function: Task_lvgl
name: lvgl
stack: 1024

View File

@ -0,0 +1,49 @@
/*
lvgl Task
*/
/* Includes ----------------------------------------------------------------- */
#include "task/user_task.h"
/* USER INCLUDE BEGIN */
#include "lvgl/lvgl.h"
#include "lvgl/stress/lv_demo_stress.h"
#include "lvgl/examples/porting/lv_port_disp_template.h"
#include "LCD/lcd.h"
/* USER INCLUDE END */
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private variables -------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private function --------------------------------------------------------- */
/* Exported functions ------------------------------------------------------- */
void Task_lvgl(void *argument) {
(void)argument; /* 未使用argument消除警告 */
lcd_init();
lv_init();
lv_port_disp_init();
lv_demo_stress();
// osDelay(LVGL_INIT_DELAY); /* 延时一段时间再开启任务 */
/* USER CODE INIT BEGIN */
/* USER CODE INIT END */
while (1) {
// lcd_fill(100, 100,200,200,RED);
lv_task_handler();
// lv_timer_handler();
/* USER CODE BEGIN */
/* USER CODE END */
}
}

View File

@ -0,0 +1,42 @@
/*
Init Task
线
*/
/* Includes ----------------------------------------------------------------- */
#include "task/user_task.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private variables -------------------------------------------------------- */
/* Private function --------------------------------------------------------- */
/* Exported functions ------------------------------------------------------- */
/**
* \brief
*
* \param argument 使
*/
void Task_Init(void *argument) {
(void)argument; /* 未使用argument消除警告 */
/* USER CODE INIT BEGIN */
/* USER CODE INIT END */
osKernelLock(); /* 锁定内核,防止任务切换 */
/* 创建任务线程 */
task_runtime.thread.lvgl = osThreadNew(Task_lvgl, NULL, &attr_lvgl);
// 创建消息队列
/* USER MESSAGE BEGIN */
task_runtime.msgq.user_msg= osMessageQueueNew(2u, 10, NULL);
/* USER MESSAGE END */
osKernelUnlock(); // 解锁内核
osThreadTerminate(osThreadGetId()); // 任务完成后结束自身
}

View File

@ -0,0 +1,16 @@
#include "task/user_task.h"
Task_Runtime_t task_runtime;
const osThreadAttr_t attr_init = {
.name = "Task_Init",
.priority = osPriorityRealtime,
.stack_size = 256 * 4,
};
/* User_task */
const osThreadAttr_t attr_lvgl = {
.name = "lvgl",
.priority = osPriorityNormal,
.stack_size = 1024 * 8,
};

View File

@ -0,0 +1,80 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <cmsis_os2.h>
#include "FreeRTOS.h"
#include "task.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Exported constants ------------------------------------------------------- */
/* 任务运行频率 */
/* 任务初始化延时ms */
#define TASK_INIT_DELAY (100u)
#define LVGL_INIT_DELAY (0)
/* Exported defines --------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/* 任务运行时结构体 */
typedef struct {
/* 各任务,也可以叫做线程 */
struct {
osThreadId_t lvgl;
} thread;
/* USER MESSAGE BEGIN */
struct {
osMessageQueueId_t user_msg; /* 用户自定义任务消息队列 */
} msgq;
/* USER MESSAGE END */
/* 机器人状态 */
struct {
float battery; /* 电池电量百分比 */
float vbat; /* 电池电压 */
float cpu_temp; /* CPU温度 */
} status;
/* USER CONFIG BEGIN */
/* USER CONFIG END */
/* 各任务的stack使用 */
struct {
UBaseType_t lvgl;
} stack_water_mark;
/* 各任务运行频率 */
struct {
} freq;
/* 任务最近运行时间 */
struct {
} last_up_time;
} Task_Runtime_t;
/* 任务运行时结构体 */
extern Task_Runtime_t task_runtime;
/* 初始化任务句柄 */
extern const osThreadAttr_t attr_init;
extern const osThreadAttr_t attr_lvgl;
/* 任务函数声明 */
void Task_Init(void *argument);
void Task_lvgl(void *argument);
#ifdef __cplusplus
}
#endif

View File

@ -43,35 +43,36 @@ Mcu.Name=STM32F407V(E-G)Tx
Mcu.Package=LQFP100 Mcu.Package=LQFP100
Mcu.Pin0=PH0-OSC_IN Mcu.Pin0=PH0-OSC_IN
Mcu.Pin1=PH1-OSC_OUT Mcu.Pin1=PH1-OSC_OUT
Mcu.Pin10=PE15 Mcu.Pin10=PE14
Mcu.Pin11=PD8 Mcu.Pin11=PE15
Mcu.Pin12=PD9 Mcu.Pin12=PD8
Mcu.Pin13=PD10 Mcu.Pin13=PD9
Mcu.Pin14=PD11 Mcu.Pin14=PD10
Mcu.Pin15=PD14 Mcu.Pin15=PD11
Mcu.Pin16=PD15 Mcu.Pin16=PD14
Mcu.Pin17=PA9 Mcu.Pin17=PD15
Mcu.Pin18=PA10 Mcu.Pin18=PA9
Mcu.Pin19=PA11 Mcu.Pin19=PA10
Mcu.Pin2=PE7 Mcu.Pin2=PB0
Mcu.Pin20=PA12 Mcu.Pin20=PA11
Mcu.Pin21=PA13 Mcu.Pin21=PA12
Mcu.Pin22=PA14 Mcu.Pin22=PA13
Mcu.Pin23=PD0 Mcu.Pin23=PA14
Mcu.Pin24=PD1 Mcu.Pin24=PD0
Mcu.Pin25=PD4 Mcu.Pin25=PD1
Mcu.Pin26=PD5 Mcu.Pin26=PD4
Mcu.Pin27=PD7 Mcu.Pin27=PD5
Mcu.Pin28=VP_FREERTOS_VS_CMSIS_V2 Mcu.Pin28=PD7
Mcu.Pin29=VP_SYS_VS_tim14 Mcu.Pin29=VP_FREERTOS_VS_CMSIS_V2
Mcu.Pin3=PE8 Mcu.Pin3=PE7
Mcu.Pin4=PE9 Mcu.Pin30=VP_SYS_VS_tim14
Mcu.Pin5=PE10 Mcu.Pin4=PE8
Mcu.Pin6=PE11 Mcu.Pin5=PE9
Mcu.Pin7=PE12 Mcu.Pin6=PE10
Mcu.Pin8=PE13 Mcu.Pin7=PE11
Mcu.Pin9=PE14 Mcu.Pin8=PE12
Mcu.PinsNb=30 Mcu.Pin9=PE13
Mcu.PinsNb=31
Mcu.ThirdPartyNb=0 Mcu.ThirdPartyNb=0
Mcu.UserConstants= Mcu.UserConstants=
Mcu.UserName=STM32F407VGTx Mcu.UserName=STM32F407VGTx
@ -80,6 +81,7 @@ MxDb.Version=DB.6.0.150
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
NVIC.CAN1_RX0_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true NVIC.CAN1_RX0_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
NVIC.CAN1_RX1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true NVIC.CAN1_RX1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
NVIC.DMA2_Stream0_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
NVIC.ForceEnableDMAVector=true NVIC.ForceEnableDMAVector=true
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
@ -109,6 +111,8 @@ PA14.Mode=Serial_Wire
PA14.Signal=SYS_JTCK-SWCLK PA14.Signal=SYS_JTCK-SWCLK
PA9.Mode=Asynchronous PA9.Mode=Asynchronous
PA9.Signal=USART1_TX PA9.Signal=USART1_TX
PB0.Locked=true
PB0.Signal=GPIO_Output
PD0.Mode=16b-d1 PD0.Mode=16b-d1
PD0.Signal=FSMC_D2 PD0.Signal=FSMC_D2
PD1.Mode=16b-d1 PD1.Mode=16b-d1
@ -199,26 +203,26 @@ RCC.EthernetFreq_Value=168000000
RCC.FCLKCortexFreq_Value=168000000 RCC.FCLKCortexFreq_Value=168000000
RCC.FamilyName=M RCC.FamilyName=M
RCC.HCLKFreq_Value=168000000 RCC.HCLKFreq_Value=168000000
RCC.HSE_VALUE=25000000 RCC.HSE_VALUE=8000000
RCC.HSI_VALUE=16000000 RCC.HSI_VALUE=16000000
RCC.I2SClocksFreq_Value=96000000 RCC.I2SClocksFreq_Value=192000000
RCC.IPParameters=48MHZClocksFreq_Value,AHBFreq_Value,APB1CLKDivider,APB1Freq_Value,APB1TimFreq_Value,APB2CLKDivider,APB2Freq_Value,APB2TimFreq_Value,CortexFreq_Value,EthernetFreq_Value,FCLKCortexFreq_Value,FamilyName,HCLKFreq_Value,HSE_VALUE,HSI_VALUE,I2SClocksFreq_Value,LSE_VALUE,LSI_VALUE,MCO2PinFreq_Value,PLLCLKFreq_Value,PLLM,PLLN,PLLQCLKFreq_Value,PLLSourceVirtual,RTCFreq_Value,RTCHSEDivFreq_Value,SYSCLKFreq_VALUE,SYSCLKSource,VCOI2SOutputFreq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,VcooutputI2S RCC.IPParameters=48MHZClocksFreq_Value,AHBFreq_Value,APB1CLKDivider,APB1Freq_Value,APB1TimFreq_Value,APB2CLKDivider,APB2Freq_Value,APB2TimFreq_Value,CortexFreq_Value,EthernetFreq_Value,FCLKCortexFreq_Value,FamilyName,HCLKFreq_Value,HSE_VALUE,HSI_VALUE,I2SClocksFreq_Value,LSE_VALUE,LSI_VALUE,MCO2PinFreq_Value,PLLCLKFreq_Value,PLLM,PLLN,PLLQCLKFreq_Value,PLLSourceVirtual,RTCFreq_Value,RTCHSEDivFreq_Value,SYSCLKFreq_VALUE,SYSCLKSource,VCOI2SOutputFreq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,VcooutputI2S
RCC.LSE_VALUE=32768 RCC.LSE_VALUE=32768
RCC.LSI_VALUE=32000 RCC.LSI_VALUE=32000
RCC.MCO2PinFreq_Value=168000000 RCC.MCO2PinFreq_Value=168000000
RCC.PLLCLKFreq_Value=168000000 RCC.PLLCLKFreq_Value=168000000
RCC.PLLM=25 RCC.PLLM=4
RCC.PLLN=336 RCC.PLLN=168
RCC.PLLQCLKFreq_Value=84000000 RCC.PLLQCLKFreq_Value=84000000
RCC.PLLSourceVirtual=RCC_PLLSOURCE_HSE RCC.PLLSourceVirtual=RCC_PLLSOURCE_HSE
RCC.RTCFreq_Value=32000 RCC.RTCFreq_Value=32000
RCC.RTCHSEDivFreq_Value=12500000 RCC.RTCHSEDivFreq_Value=4000000
RCC.SYSCLKFreq_VALUE=168000000 RCC.SYSCLKFreq_VALUE=168000000
RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
RCC.VCOI2SOutputFreq_Value=192000000 RCC.VCOI2SOutputFreq_Value=384000000
RCC.VCOInputFreq_Value=1000000 RCC.VCOInputFreq_Value=2000000
RCC.VCOOutputFreq_Value=336000000 RCC.VCOOutputFreq_Value=336000000
RCC.VcooutputI2S=96000000 RCC.VcooutputI2S=192000000
USART1.IPParameters=VirtualMode USART1.IPParameters=VirtualMode
USART1.VirtualMode=VM_ASYNC USART1.VirtualMode=VM_ASYNC
VP_FREERTOS_VS_CMSIS_V2.Mode=CMSIS_V2 VP_FREERTOS_VS_CMSIS_V2.Mode=CMSIS_V2
@ -226,3 +230,4 @@ VP_FREERTOS_VS_CMSIS_V2.Signal=FREERTOS_VS_CMSIS_V2
VP_SYS_VS_tim14.Mode=TIM14 VP_SYS_VS_tim14.Mode=TIM14
VP_SYS_VS_tim14.Signal=SYS_VS_tim14 VP_SYS_VS_tim14.Signal=SYS_VS_tim14
board=custom board=custom
rtos.0.ip=FREERTOS

View File

@ -57,6 +57,7 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
User/component/kinematics.c User/component/kinematics.c
User/component/pid.c User/component/pid.c
User/component/user_math.c User/component/user_math.c
./User/component/mixer.c
# User/device sources # User/device sources
User/device/motor.c User/device/motor.c

View File

@ -6,6 +6,10 @@ filter:
dependencies: dependencies:
- component/ahrs - component/ahrs
enabled: true enabled: true
mixer:
dependencies:
- component/user_math.h
enabled: true
pid: pid:
dependencies: dependencies:
- component/filter - component/filter

View File

@ -0,0 +1,94 @@
/*
*/
#include "mixer.h"
#include "math.h"
/**
* @brief
*
* @param mixer
* @param mode
* @return int8_t 0
*/
int8_t Mixer_Init(Mixer_t *mixer, Mixer_Mode_t mode) {
if (mixer == NULL) return -1;
mixer->mode = mode;
return 0;
}
/**
* @brief
*
* @param mixer
* @param move_vec
* @param out
* @param len
* @param scale
* @return int8_t 0
*/
int8_t Mixer_Apply(Mixer_t *mixer, MoveVector_t *move_vec, float *out,
int8_t len, float scale) {
if (mixer == NULL) return -1;
switch (mixer->mode) {
case MIXER_MECANUM:
if (len == 4) {
out[0] = move_vec->vx - move_vec->vy + move_vec->wz;
out[1] = move_vec->vx + move_vec->vy + move_vec->wz;
out[2] = -move_vec->vx + move_vec->vy + move_vec->wz;
out[3] = -move_vec->vx - move_vec->vy + move_vec->wz;
} else {
goto error;
}
break;
case MIXER_PARLFIX4:
if (len == 4) {
out[0] = -move_vec->vx;
out[1] = move_vec->vx;
out[2] = move_vec->vx;
out[3] = -move_vec->vx;
} else {
goto error;
}
case MIXER_PARLFIX2:
if (len == 2) {
out[0] = -move_vec->vx;
out[1] = move_vec->vx;
} else {
goto error;
}
case MIXER_SINGLE:
if (len == 1) {
out[0] = move_vec->vx;
} else {
goto error;
}
case MIXER_OMNICROSS:
case MIXER_OMNIPLUS:
goto error;
}
float abs_max = 0.f;
for (int8_t i = 0; i < len; i++) {
const float abs_val = fabsf(out[i]);
abs_max = (abs_val > abs_max) ? abs_val : abs_max;
}
if (abs_max > 1.f) {
for (int8_t i = 0; i < len; i++) {
out[i] /= abs_max;
}
}
for (int8_t i = 0; i < len; i++) {
out[i] *= scale;
}
return 0;
error:
for (uint8_t i = 0; i < len; i++) out[i] = 0;
return -1;
}

View File

@ -0,0 +1,76 @@
/*
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "user_math.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/** 四轮布局 */
/* 前 */
/* 2 1 */
/* 3 4 */
/* 两轮布局 */
/* 前 */
/* 2 1 */
/* 混合器模式 */
typedef enum {
MIXER_MECANUM, /* 麦克纳姆轮 */
MIXER_PARLFIX4, /* 平行四驱动轮 */
MIXER_PARLFIX2, /* 平行对侧两驱动轮 */
MIXER_OMNICROSS, /* 叉形全向轮 */
MIXER_OMNIPLUS, /* 十字全向轮 */
MIXER_SINGLE, /* 单个摩擦轮 */
} Mixer_Mode_t;
typedef struct {
Mixer_Mode_t mode;
} Mixer_t; /* 混合器主结构体 */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/**
* @brief
*
* @param mixer
* @param mode
* @return int8_t 0
*/
int8_t Mixer_Init(Mixer_t *mixer, Mixer_Mode_t mode);
/**
* @brief
*
* @param mixer
* @param move_vec
* @param out
* @param len
* @param scale
* @return int8_t 0
*/
int8_t Mixer_Apply(Mixer_t *mixer, MoveVector_t *move_vec, float *out,
int8_t len, float scale);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,418 @@
/*
*
*/
/* Includes ----------------------------------------------------------------- */
#include "chassis.h"
#include <stdlib.h>
#include "bsp/mm.h"
#include "cmsis_os2.h"
#include "component/limiter.h"
#include "device/can.h"
#include "module/cap.h"
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
#define _CAP_PERCENTAGE_WORK 100 /* 底盘不再限制功率的电容电量 */
#define _CAP_PERCENTAGE_CHARGE 30 /* 电容开始工作的电容电量 */
#define CHASSIS_MAX_CAP_POWER 100 /* 电容能够提供的最大功率 */
#define CHASSIS_ROTOR_WZ_MIN 0.6f /* 小陀螺旋转位移下界 */
#define CHASSIS_ROTOR_WZ_MAX 0.8f /* 小陀螺旋转位移上界 */
#define M_7OVER72PI (M_2PI * 7.0f / 72.0f) /* 三十五度对应弧度值 */
// #define CHASSIS_ROTOR_OMEGA 0.15f /* 小陀螺转动频率 */
// 随机产生小陀螺转动频率范围0.001-0.05
//#define CHASSIS_ROTOR_OMEGA rand() % 50 / 10000.0f + 0.001f
#define CHASSIS_ROTOR_OMEGA 0.001
/* Private macro ------------------------------------------------------------ */
/* 保证电容电量宏定义在正确范围内 */
#if ((_CAP_PERCENTAGE_WORK < 0) || (_CAP_PERCENTAGE_WORK > 100) || \
(_CAP_PERCENTAGE_CHARGE < 0) || (_CAP_PERCENTAGE_CHARGE > 100))
#error "Cap percentage should be in the range from 0 to 100."
#endif
/* 保证电容功率宏定义在正确范围内 */
#if ((CHASSIS_MAX_CAP_POWER < 60) || (CHASSIS_MAX_CAP_POWER > 200))
#error "The capacitor power should be in in the range from 60 to 200."
#endif
/* Private variables -------------------------------------------------------- */
static const float CAP_PERCENTAGE_WORK = (float)_CAP_PERCENTAGE_WORK / 100.0f;
static const float CAP_PERCENTAGE_CHARGE =
(float)_CAP_PERCENTAGE_CHARGE / 100.0f;
/* Private function -------------------------------------------------------- */
/**
* \brief
*
* \param c
* \param mode
*
* \return
*/
static int8_t Chassis_SetMode(Chassis_t *c, CMD_ChassisMode_t mode,
uint32_t now) {
if (c == NULL) return CHASSIS_ERR_NULL; /* 主结构体不能为空 */
if (mode == c->mode) return CHASSIS_OK; /* 模式未改变直接返回 */
if (mode == CHASSIS_MODE_ROTOR && c->mode != CHASSIS_MODE_ROTOR) {
srand(now);
c->wz_multi = (rand() % 2) ? -1 : 1;
}
/* 切换模式后重置PID和滤波器 */
for (uint8_t i = 0; i < c->num_wheel; i++) {
PID_Reset(c->pid.motor + i);
LowPassFilter2p_Reset(c->filter.in + i, 0.0f);
LowPassFilter2p_Reset(c->filter.out + i, 0.0f);
}
c->mode = mode;
return CHASSIS_OK;
}
/**
* @brief wz随机速度
*
* @param min wz产生最小速度
* @param max wz产生最大速度
* @param now ctrl_chassis的tick数
* @return float
*/
static float Chassis_CalcWz(const float min, const float max, uint32_t now) {
/* wz在min和max之间上限0.6f */
float wz_vary = fabs(0.2f * sinf(CHASSIS_ROTOR_OMEGA * (float)now)) + min;
return wz_vary > 0.8f ? max : wz_vary;
}
/* Exported functions ------------------------------------------------------- */
/**
* \brief
*
* \param c
* \param param
* \param target_freq
*
* \return
*/
int8_t Chassis_Init(Chassis_t *c, const Chassis_Params_t *param,
AHRS_Eulr_t *mech_zero, float target_freq) {
if (c == NULL) return CHASSIS_ERR_NULL;
c->param = param; /* 初始化参数 */
c->mode = CHASSIS_MODE_RELAX; /* 设置上电后底盘默认模式 */
c->mech_zero = mech_zero; /* 设置底盘机械零点 */
/* 如果电机反装重新计算机械零点 */
if (param->reverse.yaw) CircleReverse(&(c->mech_zero->yaw));
/* 根据参数param中的底盘型号初始化Mixer */
Mixer_Mode_t mixer_mode;
switch (c->param->type) {
case CHASSIS_TYPE_MECANUM:
c->num_wheel = 4;
mixer_mode = MIXER_MECANUM;
break;
case CHASSIS_TYPE_PARLFIX4:
c->num_wheel = 4;
mixer_mode = MIXER_PARLFIX4;
break;
case CHASSIS_TYPE_PARLFIX2:
c->num_wheel = 2;
mixer_mode = MIXER_PARLFIX2;
break;
case CHASSIS_TYPE_OMNI_CROSS:
c->num_wheel = 4;
mixer_mode = MIXER_OMNICROSS;
break;
case CHASSIS_TYPE_OMNI_PLUS:
c->num_wheel = 4;
mixer_mode = MIXER_OMNIPLUS;
break;
case CHASSIS_TYPE_SINGLE:
c->num_wheel = 1;
mixer_mode = MIXER_SINGLE;
break;
case CHASSIS_TYPE_DRONE:
/* onboard sdk. */
return CHASSIS_ERR_TYPE;
}
/* 根据底盘型号动态分配控制时使用的变量 */
c->feedback.motor_rpm =
BSP_Malloc((size_t)c->num_wheel * sizeof(*c->feedback.motor_rpm));
if (c->feedback.motor_rpm == NULL) goto error; /* 变量未分配,返回错误 */
c->feedback.motor_current =
BSP_Malloc((size_t)c->num_wheel * sizeof(*c->feedback.motor_current));
if (c->feedback.motor_current == NULL) goto error;
c->setpoint.motor_rpm =
BSP_Malloc((size_t)c->num_wheel * sizeof(*c->setpoint.motor_rpm));
if (c->setpoint.motor_rpm == NULL) goto error;
c->pid.motor = BSP_Malloc((size_t)c->num_wheel * sizeof(*c->pid.motor));
if (c->pid.motor == NULL) goto error;
c->out = BSP_Malloc((size_t)c->num_wheel * sizeof(*c->out));
if (c->out == NULL) goto error;
c->filter.in = BSP_Malloc((size_t)c->num_wheel * sizeof(*c->filter.in));
if (c->filter.in == NULL) goto error;
c->filter.out = BSP_Malloc((size_t)c->num_wheel * sizeof(*c->filter.out));
if (c->filter.out == NULL) goto error;
/* 初始化轮子电机控制PID和LPF */
for (uint8_t i = 0; i < c->num_wheel; i++) {
PID_Init(c->pid.motor + i, KPID_MODE_NO_D, target_freq,
&(c->param->motor_pid_param));
LowPassFilter2p_Init(c->filter.in + i, target_freq,
c->param->low_pass_cutoff_freq.in);
LowPassFilter2p_Init(c->filter.out + i, target_freq,
c->param->low_pass_cutoff_freq.out);
}
/* 初始化跟随云台的控制PID */
PID_Init(&(c->pid.follow), KPID_MODE_NO_D, target_freq,
&(c->param->follow_pid_param));
Mixer_Init(&(c->mixer), mixer_mode); /* 初始化混合器 */
return CHASSIS_OK;
error:
/* 动态内存分配错误时,释放已经分配的内存,返回错误值 */
BSP_Free(c->feedback.motor_rpm);
BSP_Free(c->setpoint.motor_rpm);
BSP_Free(c->pid.motor);
BSP_Free(c->out);
BSP_Free(c->filter.in);
BSP_Free(c->filter.out);
return CHASSIS_ERR_NULL;
}
/**
* \brief
*
* \param c
* \param can CAN设备结构体
*
* \return
*/
int8_t Chassis_UpdateFeedback(Chassis_t *c, const CAN_t *can) {
/* 底盘数据和CAN结构体不能为空 */
if (c == NULL) return CHASSIS_ERR_NULL;
if (can == NULL) return CHASSIS_ERR_NULL;
/* 如果电机反装重新计算正确的反馈值 */
if (c->param->reverse.yaw) {
c->feedback.gimbal_yaw_encoder =
-can->motor.gimbal.named.yaw.rotor_angle + M_2PI;
} else {
c->feedback.gimbal_yaw_encoder = can->motor.gimbal.named.yaw.rotor_angle;
}
/* 将CAN中的反馈数据写入到feedback中 */
for (uint8_t i = 0; i < c->num_wheel; i++) {
c->feedback.motor_rpm[i] = can->motor.chassis.as_array[i].rotor_speed;
c->feedback.motor_current[i] =
can->motor.chassis.as_array[i].torque_current;
}
return CHASSIS_OK;
}
/**
* \brief
*
* \param c
* \param c_cmd
* \param dt_sec
*
* \return
*/
int8_t Chassis_Control(Chassis_t *c, const CMD_ChassisCmd_t *c_cmd,
uint32_t now) {
/* 底盘数据和控制指令结构体不能为空 */
if (c == NULL) return CHASSIS_ERR_NULL;
if (c_cmd == NULL) return CHASSIS_ERR_NULL;
c->dt = (float)(now - c->lask_wakeup) / 1000.0f;
c->lask_wakeup = now;
/* 根据遥控器命令更改底盘模式 */
Chassis_SetMode(c, c_cmd->mode, now);
/* ctrl_vec -> move_vec 控制向量和真实的移动向量之间有一个换算关系 */
/* 计算vx、vy */
switch (c->mode) {
case CHASSIS_MODE_BREAK: /* 刹车模式电机停止 */
c->move_vec.vx = 0.0f;
c->move_vec.vy = 0.0f;
break;
case CHASSIS_MODE_INDENPENDENT: /* 独立模式控制向量与运动向量相等 */
c->move_vec.vx = c_cmd->ctrl_vec.vx;
c->move_vec.vy = c_cmd->ctrl_vec.vx;
break;
case CHASSIS_MODE_OPEN:
case CHASSIS_MODE_RELAX:
case CHASSIS_MODE_FOLLOW_GIMBAL: /* 按照云台方向换算运动向量 */
case CHASSIS_MODE_FOLLOW_GIMBAL_35:
case CHASSIS_MODE_ROTOR: {
float beta = c->feedback.gimbal_yaw_encoder - c->mech_zero->yaw;
float cos_beta = cosf(beta);
float sin_beta = sinf(beta);
c->move_vec.vx =
cos_beta * c_cmd->ctrl_vec.vx - sin_beta * c_cmd->ctrl_vec.vy;
c->move_vec.vy =
sin_beta * c_cmd->ctrl_vec.vx + cos_beta * c_cmd->ctrl_vec.vy;
}
}
/* 计算wz */
switch (c->mode) {
case CHASSIS_MODE_RELAX:
case CHASSIS_MODE_BREAK:
case CHASSIS_MODE_INDENPENDENT: /* 独立模式wz为0 */
c->move_vec.wz = 0.0f;
break;
case CHASSIS_MODE_OPEN:
case CHASSIS_MODE_FOLLOW_GIMBAL: /* 跟随模式通过PID控制使车头跟随云台 */
c->move_vec.wz = PID_Calc(&(c->pid.follow), c->mech_zero->yaw,
c->feedback.gimbal_yaw_encoder, 0.0f, c->dt);
break;
case CHASSIS_MODE_FOLLOW_GIMBAL_35:
c->move_vec.wz =
PID_Calc(&(c->pid.follow), c->mech_zero->yaw + M_7OVER72PI,
c->feedback.gimbal_yaw_encoder, 0.0f, c->dt);
break;
case CHASSIS_MODE_ROTOR: { /* 小陀螺模式使底盘以一定速度旋转 */
c->move_vec.wz = c->wz_multi * Chassis_CalcWz(CHASSIS_ROTOR_WZ_MIN,
CHASSIS_ROTOR_WZ_MAX, now);
}
}
/* move_vec -> motor_rpm_set. 通过运动向量计算轮子转速目标值 */
Mixer_Apply(&(c->mixer), &(c->move_vec), c->setpoint.motor_rpm, c->num_wheel,
7000.0f);
/* 根据轮子转速目标值利用PID计算电机输出值 */
for (uint8_t i = 0; i < c->num_wheel; i++) {
/* 输入滤波. */
c->feedback.motor_rpm[i] =
LowPassFilter2p_Apply(c->filter.in + i, c->feedback.motor_rpm[i]);
/* 根据底盘模式计算输出值 */
switch (c->mode) {
case CHASSIS_MODE_BREAK:
case CHASSIS_MODE_FOLLOW_GIMBAL:
case CHASSIS_MODE_FOLLOW_GIMBAL_35:
case CHASSIS_MODE_ROTOR:
case CHASSIS_MODE_INDENPENDENT: /* 独立模式,受PID控制 */
c->out[i] = PID_Calc(c->pid.motor + i, c->setpoint.motor_rpm[i],
c->feedback.motor_rpm[i], 0.0f, c->dt);
break;
case CHASSIS_MODE_OPEN: /* 开环模式,不受PID控制 */
c->out[i] = c->setpoint.motor_rpm[i] / 9000.0f;
break;
case CHASSIS_MODE_RELAX: /* 放松模式,不输出 */
c->out[i] = 0;
break;
}
/* 输出滤波. */
c->out[i] = LowPassFilter2p_Apply(c->filter.out + i, c->out[i]);
}
return CHASSIS_OK;
}
/**
* @brief
*
* @param c
* @param cap
* @param ref
* @return
*/
int8_t Chassis_PowerLimit(Chassis_t *c, const CAN_Capacitor_t *cap,
const Referee_ForChassis_t *ref) {
float power_limit = 0.0f;
if (ref->ref_status != REF_STATUS_RUNNING) {
/* 裁判系统离线,将功率限制为固定值 */
power_limit = CHASSIS_POWER_MAX_WITHOUT_REF;
} else {
if (cap->cap_status == CAN_CAP_STATUS_RUNNING &&
cap->percentage > CAP_PERCENTAGE_CHARGE) {
/* 电容在线且电量足够,使用电容 */
if (cap->percentage > CAP_PERCENTAGE_WORK) {
/* 电容接近充满时不再限制功率 */
power_limit = -1.0f;
} else {
/* 按照电容能量百分比计算输出功率 */
power_limit = ref->chassis_power_limit +
(cap->percentage - CAP_PERCENTAGE_CHARGE) /
(CAP_PERCENTAGE_WORK - CAP_PERCENTAGE_CHARGE) *
(float)CHASSIS_MAX_CAP_POWER;
}
} else {
/* 电容不在工作,根据缓冲能量计算输出功率限制 */
power_limit = PowerLimit_TargetPower(ref->chassis_power_limit,
ref->chassis_pwr_buff);
}
}
/* 应用功率限制 */
PowerLimit_ChassicOutput(power_limit, c->out, c->feedback.motor_rpm,
c->num_wheel);
return CHASSIS_OK;
}
/**
* \brief
*
* \param s
* \param out CAN设备底盘输出结构体
*/
void Chassis_DumpOutput(Chassis_t *c, CAN_ChassisOutput_t *out) {
for (uint8_t i = 0; i < c->num_wheel; i++) {
out->as_array[i] = c->out[i];
}
}
/**
* \brief Chassis输出数据
*
* \param out CAN设备底盘输出结构体
*/
void Chassis_ResetOutput(CAN_ChassisOutput_t *out) {
for (uint8_t i = 0; i < 4; i++) {
out->as_array[i] = 0.0f;
}
}
/**
* @brief
*
* @param chassis
* @param ui UI数据结构体
*/
void Chassis_DumpUI(const Chassis_t *c, Referee_ChassisUI_t *ui) {
ui->mode = c->mode;
ui->angle = c->feedback.gimbal_yaw_encoder - c->mech_zero->yaw;
}

View File

@ -0,0 +1,179 @@
/*
*
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "component/filter.h"
#include "component/mixer.h"
#include "component/pid.h"
#include "bsp/can.h"
#include "device/referee.h"
/* Exported constants ------------------------------------------------------- */
#define CHASSIS_OK (0) /* 运行正常 */
#define CHASSIS_ERR (-1) /* 运行时发现了其他错误 */
#define CHASSIS_ERR_NULL (-2) /* 运行时发现NULL指针 */
#define CHASSIS_ERR_MODE (-3) /* 运行时配置了错误的CMD_ChassisMode_t */
#define CHASSIS_ERR_TYPE (-4) /* 运行时配置了错误的Chassis_Type_t */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/* 底盘类型(底盘的物理设计) */
typedef enum {
CHASSIS_TYPE_MECANUM, /* 麦克纳姆轮 */
CHASSIS_TYPE_PARLFIX4, /* 平行摆设的四个驱动轮 */
CHASSIS_TYPE_PARLFIX2, /* 平行摆设的两个驱动轮 */
CHASSIS_TYPE_OMNI_CROSS, /* 叉型摆设的四个全向轮 */
CHASSIS_TYPE_OMNI_PLUS, /* 十字型摆设的四个全向轮 */
CHASSIS_TYPE_DRONE, /* 底盘为无人机 */
CHASSIS_TYPE_SINGLE, /* 单个摩擦轮 */
} Chassis_Type_t;
/* 底盘参数的结构体包含所有初始化用的参数通常是const存好几组 */
typedef struct {
Chassis_Type_t type; /* 底盘类型,底盘的机械设计和轮子选型 */
KPID_Params_t motor_pid_param; /* 轮子控制PID的参数 */
KPID_Params_t follow_pid_param; /* 跟随云台PID的参数 */
/* 低通滤波器截止频率 */
struct {
float in; /* 输入 */
float out; /* 输出 */
} low_pass_cutoff_freq;
/* 电机反装 应该和云台设置相同*/
struct {
bool yaw;
} reverse;
} Chassis_Params_t;
/*
*
*
*/
typedef struct {
uint32_t lask_wakeup;
float dt;
const Chassis_Params_t *param; /* 底盘的参数用Chassis_Init设定 */
AHRS_Eulr_t *mech_zero;
/* 模块通用 */
CMD_ChassisMode_t mode; /* 底盘模式 */
/* 底盘设计 */
int8_t num_wheel; /* 底盘轮子数量 */
Mixer_t mixer; /* 混合器,移动向量->电机目标值 */
MoveVector_t move_vec; /* 底盘实际的运动向量 */
/* 反馈信息 */
struct {
float gimbal_yaw_encoder; /* 云台Yaw轴编码器角度 */
float *motor_rpm; /* 电机转速的动态数组单位RPM */
float *motor_current; /* 转矩电流 单位A */
} feedback;
float wz_multi; /* 小陀螺模式旋转方向 */
/* PID计算的目标值 */
struct {
float *motor_rpm; /* 电机转速的动态数组单位RPM */
} setpoint;
/* 反馈控制用的PID */
struct {
KPID_t *motor; /* 控制轮子电机用的PID的动态数组 */
KPID_t follow; /* 跟随云台用的PID */
} pid;
/* 滤波器 */
struct {
LowPassFilter2p_t *in; /* 反馈值滤波器 */
LowPassFilter2p_t *out; /* 输出值滤波器 */
} filter;
float *out; /* 电机最终的输出值的动态数组 */
} Chassis_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* \brief
*
* \param c
* \param param
* \param target_freq
*
* \return
*/
int8_t Chassis_Init(Chassis_t *c, const Chassis_Params_t *param,
AHRS_Eulr_t *mech_zero, float target_freq);
/**
* \brief
*
* \param c
* \param can CAN设备结构体
*
* \return
*/
int8_t Chassis_UpdateFeedback(Chassis_t *c, const CAN_t *can);
/**
* \brief
*
* \param c
* \param c_cmd
* \param dt_sec
*
* \return
*/
int8_t Chassis_Control(Chassis_t *c, const CMD_ChassisCmd_t *c_cmd,
uint32_t now);
/**
* @brief
*
* @param c
* @param cap
* @param ref
* @return
*/
int8_t Chassis_PowerLimit(Chassis_t *c, const CAN_Capacitor_t *cap,
const Referee_ForChassis_t *ref);
/**
* \brief
*
* \param s
* \param out CAN设备底盘输出结构体
*/
void Chassis_DumpOutput(Chassis_t *c, CAN_ChassisOutput_t *out);
/**
* \brief Chassis输出数据
*
* \param out CAN设备底盘输出结构体
*/
void Chassis_ResetOutput(CAN_ChassisOutput_t *out);
/**
* @brief
*
* @param chassis
* @param ui UI数据结构体
*/
void Chassis_DumpUI(const Chassis_t *c, Referee_ChassisUI_t *ui);
#ifdef __cplusplus
}
#endif

View File

@ -5,3 +5,9 @@
function: Task_control function: Task_control
name: control name: control
stack: 1024 stack: 1024
- delay: 0
description: ''
freq_control: false
function: Task_display
name: display
stack: 1024

View File

@ -4,9 +4,6 @@
*/ */
/* Includes ----------------------------------------------------------------- */ /* Includes ----------------------------------------------------------------- */
#include "component/filter.h"
#include "component/pid.h"
#include "device/motor_dm.h"
#include "task/user_task.h" #include "task/user_task.h"
/* USER INCLUDE BEGIN */ /* USER INCLUDE BEGIN */
#include "component/kinematics.h" #include "component/kinematics.h"
@ -99,33 +96,14 @@ MOTOR_LZ_t* a;
void Task_control(void *argument) { void Task_control(void *argument) {
(void)argument; /* 未使用argument消除警告 */ (void)argument; /* 未使用argument消除警告 */
/* 计算任务运行到指定频率需要等待的tick数 */ /* 计算任务运行到指定频率需要等待的tick数 */
const uint32_t delay_tick = osKernelGetTickFreq() / CONTROL_FREQ; const uint32_t delay_tick = osKernelGetTickFreq() / CONTROL_FREQ;
osDelay(CONTROL_INIT_DELAY); /* 延时一段时间再开启任务 */ osDelay(CONTROL_INIT_DELAY); /* 延时一段时间再开启任务 */
BSP_CAN_Init();
MOTOR_LZ_Init();
MOTOR_LZ_Register(&lzmotor.param);
MOTOR_LZ_Enable(&lzmotor.param);
a = MOTOR_LZ_GetMotor(&lzmotor.param);
// MOTOR_DM_Register(&motor.param);
// MOTOR_DM_Enable(&motor.param);
// PID_Init(&pid, KPID_MODE_SET_D, 1000, &pid_pram);
// LowPassFilter2p_Init(&filter, 1000, 20);
// LPF_Init(&ff, 1, 5);
//
// motor_out.angle = tp;
// motor_out.velocity = 0;
// motor_out.kp = 0;
// motor_out.kd = 0;
// motor_out.torque = 0;
// float sp = 0;
robot_init(&hand, LINK, T_mat, mat_pdata, 5);
fkine(&hand);
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */ uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
/* USER CODE INIT BEGIN * /* USER CODE INIT BEGIN */
/* USER CODE INIT END */ /* USER CODE INIT END */
@ -151,4 +129,5 @@ a = MOTOR_LZ_GetMotor(&lzmotor.param);
/* USER CODE END */ /* USER CODE END */
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */ osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
} }
} }

View File

@ -0,0 +1,38 @@
/*
display Task
*/
/* Includes ----------------------------------------------------------------- */
#include "task/user_task.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private variables -------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private function --------------------------------------------------------- */
/* Exported functions ------------------------------------------------------- */
void Task_display(void *argument) {
(void)argument; /* 未使用argument消除警告 */
osDelay(DISPLAY_INIT_DELAY); /* 延时一段时间再开启任务 */
/* USER CODE INIT BEGIN */
/* USER CODE INIT END */
while (1) {
/* USER CODE BEGIN */
/* USER CODE END */
}
}

View File

@ -31,6 +31,7 @@ void Task_Init(void *argument) {
/* 创建任务线程 */ /* 创建任务线程 */
task_runtime.thread.control = osThreadNew(Task_control, NULL, &attr_control); task_runtime.thread.control = osThreadNew(Task_control, NULL, &attr_control);
task_runtime.thread.display = osThreadNew(Task_display, NULL, &attr_display);
// 创建消息队列 // 创建消息队列
/* USER MESSAGE BEGIN */ /* USER MESSAGE BEGIN */

View File

@ -14,3 +14,8 @@ const osThreadAttr_t attr_control = {
.priority = osPriorityNormal, .priority = osPriorityNormal,
.stack_size = 1024 * 4, .stack_size = 1024 * 4,
}; };
const osThreadAttr_t attr_display = {
.name = "display",
.priority = osPriorityNormal,
.stack_size = 1024 * 4,
};

View File

@ -18,6 +18,7 @@ extern "C" {
/* 任务初始化延时ms */ /* 任务初始化延时ms */
#define TASK_INIT_DELAY (100u) #define TASK_INIT_DELAY (100u)
#define CONTROL_INIT_DELAY (0) #define CONTROL_INIT_DELAY (0)
#define DISPLAY_INIT_DELAY (0)
/* Exported defines --------------------------------------------------------- */ /* Exported defines --------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */ /* Exported macro ----------------------------------------------------------- */
@ -28,6 +29,7 @@ typedef struct {
/* 各任务,也可以叫做线程 */ /* 各任务,也可以叫做线程 */
struct { struct {
osThreadId_t control; osThreadId_t control;
osThreadId_t display;
} thread; } thread;
/* USER MESSAGE BEGIN */ /* USER MESSAGE BEGIN */
@ -50,6 +52,7 @@ typedef struct {
/* 各任务的stack使用 */ /* 各任务的stack使用 */
struct { struct {
UBaseType_t control; UBaseType_t control;
UBaseType_t display;
} stack_water_mark; } stack_water_mark;
/* 各任务运行频率 */ /* 各任务运行频率 */
@ -70,10 +73,12 @@ extern Task_Runtime_t task_runtime;
/* 初始化任务句柄 */ /* 初始化任务句柄 */
extern const osThreadAttr_t attr_init; extern const osThreadAttr_t attr_init;
extern const osThreadAttr_t attr_control; extern const osThreadAttr_t attr_control;
extern const osThreadAttr_t attr_display;
/* 任务函数声明 */ /* 任务函数声明 */
void Task_Init(void *argument); void Task_Init(void *argument);
void Task_control(void *argument); void Task_control(void *argument);
void Task_display(void *argument);
#ifdef __cplusplus #ifdef __cplusplus
} }