STM32
直播中

杨雪

7年用户 1103经验值
私信 关注
[问答]

基于STM32F103C8T6的LiteOS是如何去移植的

基于STM32F103C8T6的LiteOS是如何去移植的? 移植LiteOS有哪几种方案呢?

回帖(2)

黄彩萍

2021-11-29 09:33:59
总览   

     本文基于STM32F103C8T6,详细讲述华为LiteOS的移植过程。开发工具是MDK5。LiteOS官方已经适配过cortex M系列内核的单片机,因此移植过程非常简单。
    LiteOS有两种移植方案:OS接管中断和非接管中断方式。接管中断的方式,是由LiteOS创建很管理中断,需要修改stm32启动文件,移植比较复杂。STM32的中断管理做的很好,用不着由LiteOS管理中断,所以我们下边的移植方案,都是非接管中断的方式的。中断的使用,跟在裸机工程时是一样的。
   在target_config.h 中将 LOSCFG_PLATFORM_HWI 宏定义为 NO,即为不接管中断方式。该值默认为NO 。
  移植的主要步骤如下:
1、添加内核文件






2、配置头文件






3、移除systick和pendsv中断

4、修改target_config.h

5、重定向printf函数(一般在裸机工程中就会实现)

说明:内核运行过程中会通过串口打印一些错误信息。如果日志功能开启、而又没有重定向printf函数的话,则会导致日志打印出错,程序异常卡死。之前我就是没有重定向printf函数,结果出了莫名其妙的问题,程序异常卡死在创建任务的地方。
下边我们通过新建一个裸机工程,一步步讲解如何进行移植。以下是详细过程。
一、创建裸机工程

我们这次使用的是一个STM32F103C8T6的最小系统板,板载有三个LED、一个串口。LED连接引脚为(PB5PB6PB7),低电平点亮;串口为USART1(PA9,PA10),采用DMA+空闲中断的方式接收数据。我们利用STM32CubeMX来生成裸机工程(STM32CubeMX的使用本文不详细描述),设置如下:
1、引脚配置




  • 配置PB5PB6PB7为推挽输出方式;
  • 配置PA9PA10为USART1复用功能;
  • 配置PA13为SWDIO功能,PA14为SWCLK功能(下载及调试)
  • 使能串行调试功能











2、时钟配置






3、串口配置






4、生成代码

勾选生成对应外设驱动的‘.c/.h’文件,生成代码。
打开工程,加入LED开关状态的宏定义和串口空闲中断接收的代码,具体如下(当然,如果你不使用DMA+空闲中断的方式,也可以不进行下边2中的修改,但是一定要重定向printf函数):
1、在main.h中加入LED宏定义代码。

1 #define LED1_ON()  HAL_GPIO_WritePin(GPIOB, LED1_Pin, GPIO_PIN_RESET)2 #define LED1_OFF() HAL_GPIO_WritePin(GPIOB, LED1_Pin, GPIO_PIN_SET)3 4 #define LED2_ON()  HAL_GPIO_WritePin(GPIOB, LED2_Pin, GPIO_PIN_RESET)5 #define LED2_OFF() HAL_GPIO_WritePin(GPIOB, LED2_Pin, GPIO_PIN_SET)6  7 #define LED3_ON()  HAL_GPIO_WritePin(GPIOB, LED3_Pin, GPIO_PIN_RESET)8 #define LED3_OFF() HAL_GPIO_WritePin(GPIOB, LED3_Pin, GPIO_PIN_SET)2、实现串口空闲中断接收
在usart.h中加入如下代码:


1 #define UART1_BUFF_SIZE     256 //串口接收缓存区长度 2 typedef struct   3 {  4   uint8_t  RxFlag;            //空闲接收标记   5   uint16_t RxLen;             //接收长度   6   uint8_t  *RxBuff;           //DMA接收缓存   7 }USART_RECEIVETYPE;  8 extern USART_RECEIVETYPE Uart1Rx; 9 void USART1_ReceiveIDLE(void);10 void UART_SendData(USART_TypeDef * Uart,uint8_t *buff,uint16_t size);11 在usart.c中加入如下代码12 static uint8_t Uar1tRxBuff[UART1_BUFF_SIZE+1]; //定义串口接收buffer13 USART_RECEIVETYPE Uart1Rx = {14                      .RxBuff = Uar1tRxBuff,15                    };16  17 void USART1_ReceiveIDLE(void)  18 {  19     uint32_t temp;  20     if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET))  21     {22         __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE); 23         temp = huart1.Instance->SR;24         temp = huart1.Instance->DR;25         HAL_UART_DMAStop(&huart1);  26         temp = huart1.hdmarx->Instance->CNDTR;  27         Uart1Rx.RxLen =  UART1_BUFF_SIZE - temp;   28         Uart1Rx.RxFlag=1; 29         Uart1Rx.RxBuff[Uart1Rx.RxLen] = 0;30         HAL_UART_Receive_DMA(&huart1,Uart1Rx.RxBuff,UART1_BUFF_SIZE);  31     } 32 }33 void UART_SendByte(USART_TypeDef * Uart,uint8_t data)34 {     35     Uart->DR = data;36 while((Uart->SR&UART_FLAG_TXE)==0);37 while((Uart->SR&UART_FLAG_TC)==0);       38 }39 void UART_SendData(USART_TypeDef * Uart,uint8_t *buff,uint16_t size)40 {41     while(size--)42 {43 Uart->DR = *(buff++);44 while((Uart->SR&UART_FLAG_TXE)==0);45 }46     while((Uart->SR&UART_FLAG_TC)==0);       47 }48 ///重定向c库函数printf到USART149 int fputc(int ch, FILE *f)50 {51     /* 发送一个字节数据到USART1 */52     UART_SendByte(USART1, (uint8_t) ch);53     return (ch);54 }55  56 ///重定向c库函数scanf到USART157 int fgetc(FILE *f)58 {59     /* 等待串口1输入数据 */60     while((USART1->SR&UART_FLAG_RXNE)==0);61     return (int)USART1->DR&0xff;62 }


修改void MX_USART1_UART_Init(void),在最后加入以下代码:
1 //add for DMA.Idle interrupt2   __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE); 3   __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_TC); 4   HAL_UART_Receive_DMA(&huart1, Uart1Rx.RxBuff, UART1_BUFF_SIZE); //开启DMA接收 5   __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);               //使能空闲中断在stm32f1xx_it.c中声明USART1_ReceiveIDLE,并在串口中断中调用该函数:


1 void USART1_ReceiveIDLE(void);  2    3 void USART1_IRQHandler(void)  4 {  5   /* USER CODE BEGIN USART1_IRQn 0 */  6   USART1_ReceiveIDLE();  7   /* USER CODE END USART1_IRQn 0 */  8   HAL_UART_IRQHandler(&huart1);  9   /* USER CODE BEGIN USART1_IRQn 1 */ 10   11   /* USER CODE END USART1_IRQn 1 */ 12 }

3、在main.c的main中添加代码验证裸机工程


1  while (1)  2   {  3     /* USER CODE END WHILE */  4    5     /* USER CODE BEGIN 3 */  6       LED1_ON();  7       LED2_ON();  8       LED3_ON();  9       HAL_Delay(300); 10       LED1_OFF(); 11       LED2_OFF(); 12       LED3_OFF(); 13       HAL_Delay(300); 14       printf("This is the uart test!rn"); 15       if(Uart1Rx.RxFlag){ 16           Uart1Rx.RxFlag = 0; 17           UART_SendData(USART1,Uart1Rx.RxBuff,Uart1Rx.RxLen); 18       } 19   }
编译下载代码,程序正常运行,LED闪烁,同时打印字符串。
经过上述操作,我们已经完成了裸机工程的准备工作。
二、内核移植

1、下载LiteOS

注:LiteOS 最新特性都存放在 develop 分支中,建议取该分支代码进行学习。本文的代码即为 develop分支代码。
点击链接进入LiteOS代码仓库首页,切换至develop分支,点击右侧“Clone or download”按钮,选择Download ZIP,下载代码,如下图所示:





LiteOS内核代码目录结构如下图所示:


举报

黄芙蓉

2021-11-29 09:34:05
2、拷贝内核代码

在工程目录下新建LiteOS文件夹(文件夹名称个人自定义),从上一步下载的LiteOS内核源码中,将arch、kernel、targetsSTM32F103VET6_NB_GCCOS_CONFIG 拷贝至LiteOS文件夹内,如下图所示:





arch 中是CPU架构相关的代码;kernel是LiteOS内核代码;OS_CONFIG中是配置内核功能的头文件,可用于裁剪内核功能,我们从官方提供的例程中拷贝过来(可从target文件夹给出的例子中任意拷贝一个)。
3、向MDK工程添加内核文件

打开MDK工程,打开Mange Project Items。



  • 添加arch分组

在Groups添加 LiteOS/Arch分组,添加以下文件:

1 archarmarm-msrc 目录下的全部文件:2     los_hw.c3     los_hw_tick.c4     los_hwi.c5 archarmarm-mcortex-m3keil 目录下的:6     los_dispatch_keil.S

如下图所示:





注:点击AddFiles时,MDK默认添加.c类型的文件。los_dispatch_keil.S是汇编文件,因此在添加时,需要将文件类型选择为All files。



  • 添加kernel分组

在Groups添加 LiteOS/kernel分组,添加以下文件:

1 kernelbasecore  下面全部 .c 文件2 kernelbaseipc   下面全部 .c 文件3 kernelbasemembestfit_little 下面全部 .c 文件4 kernelbasememcommon 下面全部 .c 文件5 kernelbasememmembox 下面全部 .c 文件6 kernelbasemisc 下面全部 .c 文件7 kernelbaseom 下面全部 .c 文件8 kernelextendedtickless 下面全部 .c 文件 (如不使用tickless,可不添加)9 kernel 下面的 los_init.c




说明:liteos提供三套动态内存算法,位于kernel/base/mem目录下,分别为bestfit、bestfit_little、tlsf,我们本次移植的是bestfit_little.可根据需求移植其他的算法。kernelbasememmembox目录下是 LiteOS 提供的静态内存算法,与动态内存算法不冲突。
4、配置头文件

如下图所示,依次点击1、2、3,打开头文件配置窗口:





头文件配置如下图所示:





需要添加的头文件路径为:

1 archarmarm-minclude2  3 kernelinclude4  5 kernelbaseinclude6  7 kernelextendedinclude8  9 OS_CONFIG

5、移除Systick和pendsv中断

打开stm32f1xx_it.c,找到 SysTick_Handler 和 PendSV_Handler
将这两个中断处理函数屏蔽掉。否则会出现如下编译错误。





说明:liteos内核使用到了systick和pendsv这两个中断,并在内核代码中有对应实现
6、修改target_config.h

OS_CONFIG/target_config.h 文件,该文件主要用于配置MCU驱动头文件、RAM大小、内核功能等,需要根据自己的环境进行修改。
我们主要需要修改以下两处:



  • MCU驱动头文件






根据使用的MCU,包含对应的头文件。



  • SRAM大小






根据使用的MCU芯片SRAM大小进行修改。
这里我们使用的是STM32F103C8T6,其SRAM为20KB。



  • 不接管中断

设置LOSCFG_PLATFORM_HWI 宏定义为 NO(该值默认为NO,一般无需修改,出于谨慎,移植过来还是要检查下)





target_config.h 文件还有很多其他宏定义,主要是配置内核的功能。比如是否使用队列、软件定时器、是否使用时间片、信号量等。
经过以上的操作,LiteOS的移植就完成了。点击编译。
7、创建一个任务

经过前面的操作,移植工作就完成了,这里我们可以创建一个任务,使用LiteOS。在下边的例子中,我们创建了两个任务,一个任务按照2S的周期点亮LED1,另外一个任务按照400毫秒的周期点亮LED2。以下是代码实现:

1 /* Includes ------------------------------------------------------------------*/   2 #include "main.h"   3 #include "dma.h"   4 #include "usart.h"   5 #include "gpio.h"   6     7 /* Private includes ----------------------------------------------------------*/   8 /* USER CODE BEGIN Includes */   9 #include "los_sys.h"  10 #include "los_task.ph"  11 #include "los_memory.ph"  12 /* USER CODE END Includes */  13 /* Private function prototypes -----------------------------------------------*/  14 void SystemClock_Config(void);  15 /* USER CODE BEGIN PFP */  16    17 /* USER CODE END PFP */  18    19 /* Private user code ---------------------------------------------------------*/  20 /* USER CODE BEGIN 0 */  21 static void Led1Task(void)  22 {  23     while(1) {  24         LED1_ON();  25         LOS_TaskDelay(1000);  26         LED1_OFF();  27         LOS_TaskDelay(1000);  28     }  29 }  30 static void Led2Task(void)  31 {  32     while(1) {  33         LED2_ON();  34         LOS_TaskDelay(200);  35         LED2_OFF();  36         LOS_TaskDelay(200);  37     }  38 }  39 UINT32 RX_Task_Handle;  40 UINT32 TX_Task_Handle;  41 static UINT32 AppTaskCreate(void)  42 {  43 UINT32 uwRet = LOS_OK;  44     TSK_INIT_PARAM_S task_init_param;  45    46 task_init_param.usTaskPrio = 4;  47 task_init_param.pcName = "RxTask";  48 task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Led1Task;  49 task_init_param.uwStackSize = 512;  50 uwRet = LOS_TaskCreate(&RX_Task_Handle, &task_init_param);  51     if (uwRet != LOS_OK)  52     {  53         printf("Led1Task create failed,%Xn",uwRet);  54         return uwRet;  55     }  56       57     task_init_param.usTaskPrio = 4;  58 task_init_param.pcName = "TxTask";  59 task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Led2Task;  60 task_init_param.uwStackSize = 512;  61 uwRet = LOS_TaskCreate(&TX_Task_Handle, &task_init_param);  62     if (uwRet != LOS_OK)  63     {  64         printf("Led2Task create failed,%Xn",uwRet);  65         return uwRet;  66     }  67 return LOS_OK;  68 }  69 /* USER CODE END 0 */  70    71 /**  72   * @brief  The application entry point.  73   * @retval int  74   */  75 int main(void)  76 {  77   /* USER CODE BEGIN 1 */  78     UINT32 uwRet = LOS_OK;  79    80   /* USER CODE END 1 */  81     82    83   /* MCU Configuration--------------------------------------------------------*/  84    85   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */  86   HAL_Init();  87    88   /* USER CODE BEGIN Init */  89    90   /* USER CODE END Init */  91    92   /* Configure the system clock */  93   SystemClock_Config();  94    95   /* USER CODE BEGIN SysInit */  96    97   /* USER CODE END SysInit */  98    99   /* Initialize all configured peripherals */ 100   MX_GPIO_Init(); 101   MX_DMA_Init(); 102   MX_USART1_UART_Init(); 103   /* USER CODE BEGIN 2 */ 104   LOS_KernelInit(); 105   uwRet = AppTaskCreate(); 106   if(uwRet != LOS_OK) { 107       printf("LOS Creat task failedrn"); 108       //return LOS_NOK; 109   } 110   LOS_Start(); 111   /* USER CODE END 2 */ 112   113   /* Infinite loop */ 114   /* USER CODE BEGIN WHILE */ 115   while (1) 116   { 117     /* USER CODE END WHILE */ 118   119     /* USER CODE BEGIN 3 */ 120       //code below are used to verify the hardware. 121       LED1_ON(); 122       LED2_ON(); 123       LED3_ON(); 124       HAL_Delay(300); 125       LED1_OFF(); 126       LED2_OFF(); 127       LED3_OFF(); 128       HAL_Delay(300); 129       printf("This is the uart test!rn"); 130   } 131   /* USER CODE END 3 */ 132 } 133   134 /** 135   * @brief System Clock Configuration 136   * @retval None 137   */ 138 void SystemClock_Config(void) 139 { 140   RCC_OscInitTypeDef RCC_OscInitStruct = {0}; 141   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; 142   143   /** Initializes the CPU, AHB and APB busses clocks 144   */145   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;146   RCC_OscInitStruct.HSIState = RCC_HSI_ON;147   RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;148   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;149   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;150   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;151   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)152   {153     Error_Handler();154   }155   /** Initializes the CPU, AHB and APB busses clocks 156   */157   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK158                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;159   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;160   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;161   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;162   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;163  164   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)165   {166     Error_Handler();167   }168 }169  170 /* USER CODE BEGIN 4 */171  172 /* USER CODE END 4 */173  174 /**175   * @brief  This function is executed in case of error occurrence.176   * @retval None177   */178 void Error_Handler(void)179 {180   /* USER CODE BEGIN Error_Handler_Debug */181   /* User can add his own implementation to report the HAL error return state */182  183   /* USER CODE END Error_Handler_Debug */184 }185  186 #ifdef  USE_FULL_ASSERT187 /**188   * @brief  Reports the name of the source file and the source line number189   *         where the assert_param error has occurred.190   * @param  file: pointer to the source file name191   * @param  line: assert_param error line source number192   * @retval None193   */194 void assert_failed(uint8_t *file, uint32_t line)195 { 196   /* USER CODE BEGIN 6 */197   /* User can add his own implementation to report the file name and line number,198      tex: printf("Wrong parameters value: file %s on line %drn", file, line) */199   /* USER CODE END 6 */200 }201 #endif /* USE_FULL_ASSERT */
附件为移植好的工程代码。
(代码中有串口空闲中断+DMA的样例代码,可参考。利用串口空闲中断,可以很好的实现数据分帧)
举报

更多回帖

发帖
×
20
完善资料,
赚取积分