【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連接引腳為(PB5\PB6\PB7),低電平點亮;串口為USART1(PA9,PA10),采用DMA+空閑中斷的方式接收數據。我們利用STM32CubeMX來生成裸機工程(STM32CubeMX的使用本文不詳細描述),設置如下:

1、引腳配置

  • 配置PB5\PB6\PB7為推挽輸出方式;
  • 配置PA9\PA10為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!\r\n");
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、targets\STM32F103VET6_NB_GCC\OS_CONFIG 拷貝至LiteOS文件夾內,如下圖所示:

arch 中是CPU架構相關的代碼;kernel是LiteOS內核代碼;OS_CONFIG中是配置內核功能的頭文件,可用於裁剪內核功能,我們從官方提供的例程中拷貝過來(可從target文件夾給出的例子中任意拷貝一個)。

3、向MDK工程添加內核文件

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

  • 添加arch分組

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

1 arch\arm\arm-m\src 目錄下的全部文件:
2     los_hw.c
3     los_hw_tick.c
4     los_hwi.c
5 arch\arm\arm-m\cortex-m3\keil 目錄下的:
6     los_dispatch_keil.S

如下圖所示:

注:點擊AddFiles時,MDK默認添加.c類型的文件。los_dispatch_keil.S是匯編文件,因此在添加時,需要將文件類型選擇為All files。

  • 添加kernel分組

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

1 kernel\base\core  下面全部 .c 文件
2 kernel\base\ipc   下面全部 .c 文件
3 kernel\base\mem\bestfit_little 下面全部 .c 文件
4 kernel\base\mem\common 下面全部 .c 文件
5 kernel\base\mem\membox 下面全部 .c 文件
6 kernel\base\misc 下面全部 .c 文件
7 kernel\base\om 下面全部 .c 文件
8 kernel\extended\tickless 下面全部 .c 文件 (如不使用tickless,可不添加)
9 kernel 下面的 los_init.c

說明:liteos提供三套動態內存算法,位於kernel/base/mem目錄下,分別為bestfit、bestfit_little、tlsf,我們本次移植的是bestfit_little.可根據需求移植其他的算法。kernel\base\mem\membox目錄下是 LiteOS 提供的靜態內存算法,與動態內存算法不沖突。

4、配置頭文件

如下圖所示,依次點擊1、2、3,打開頭文件配置窗口:

頭文件配置如下圖所示:

需要添加的頭文件路徑為:

1 arch\arm\arm-m\include
2  
3 kernel\include
4  
5 kernel\base\include
6  
7 kernel\extended\include
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\n",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\n",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\r\n");
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!\r\n");
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\r\n", 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開放能力的最佳舞台。

歡迎報名參會


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM