zoukankan      html  css  js  c++  java
  • 【LiteOS】STM32F103-LiteOS移植教程(详细篇)【华为云技术分享】

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/devcloud/article/details/102543764

    总览

    本文基于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]; //定义串口接收buffer
    13 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到USART1
    49 int fputc(int ch, FILE *f)
    50 {
    51     /* 发送一个字节数据到USART1 */
    52     UART_SendByte(USART1, (uint8_t) ch);
    53     return (ch);
    54 }
    55  
    56 ///重定向c库函数scanf到USART1
    57 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 interrupt
    2   __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!
    ");
    15       if(Uart1Rx.RxFlag){
    16           Uart1Rx.RxFlag = 0;
    17           UART_SendData(USART1,Uart1Rx.RxBuff,Uart1Rx.RxLen);
    18       }
    19   }

    编译下载代码,程序正常运行,LED闪烁,同时打印字符串。

    经过上述操作,我们已经完成了裸机工程的准备工作。

    二、内核移植

    1、下载LiteOS

    LiteOS 开源代码路径:https://github.com/LiteOS/LiteOS

    注:LiteOS 最新特性都存放在 develop 分支中,建议取该分支代码进行学习。本文的代码即为 develop分支代码。

    点击链接进入LiteOS代码仓库首页,切换至develop分支,点击右侧“Clone or download”按钮,选择Download ZIP,下载代码,如下图所示:

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

    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.c
    3     los_hw_tick.c
    4     los_hwi.c
    5 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 kernelbasememestfit_little 下面全部 .c 文件
    4 kernelbasememcommon 下面全部 .c 文件
    5 kernelbasememmembox 下面全部 .c 文件
    6 kernelbasemisc 下面全部 .c 文件
    7 kernelbaseom 下面全部 .c 文件
    8 kernelextended	ickless 下面全部 .c 文件 (如不使用tickless,可不添加)
    9 kernel 下面的 los_init.c

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

    4、配置头文件

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

    头文件配置如下图所示:

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

    1 archarmarm-minclude
    2  
    3 kernelinclude
    4  
    5 kernelbaseinclude
    6  
    7 kernelextendedinclude
    8  
    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,%X
    ",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,%X
    ",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 failed
    ");
    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!
    ");
    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_SYSCLK
    158                               |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 None
    177   */
    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_ASSERT
    187 /**
    188   * @brief  Reports the name of the source file and the source line number
    189   *         where the assert_param error has occurred.
    190   * @param  file: pointer to the source file name
    191   * @param  line: assert_param error line source number
    192   * @retval None
    193   */
    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 %d
    ", file, line) */
    199   /* USER CODE END 6 */
    200 }
    201 #endif /* USE_FULL_ASSERT */

    附件为移植好的工程代码。

    (代码中有串口空闲中断+DMA的样例代码,可参考。利用串口空闲中断,可以很好的实现数据分帧)

    LiteosPorting.rar

    作者:llb90

    HDC.Cloud 华为开发者大会2020 即将于2020年2月11日-12日在深圳举办,是一线开发者学习实践鲲鹏通用计算、昇腾AI计算、数据库、区块链、云原生、5G等ICT开放能力的最佳舞台。

    欢迎报名参会

  • 相关阅读:
    指针与强制类型转换
    String 转Clob
    Oracle数据类型对应Java类型
    Oracle常见的问题
    面对批量更新之字符串的拼接
    java使用sigar 遇到问题的解决方案
    关于IE8以上 不引人css 症状
    java Springmvc ajax上传
    再也不要看到Eclipse万恶的arg0,arg1提示
    TextView实现跑马灯效果
  • 原文地址:https://www.cnblogs.com/huaweicloud/p/11867560.html
Copyright © 2011-2022 走看看