【LiteOS】STM32F103-LiteOS移植教程(详细篇)【华为云技术分享】
版權聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權協(xié)議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/devcloud/article/details/102543764
總覽
本文基于STM32F103C8T6,詳細講述華為LiteOS的移植過程。開發(fā)工具是MDK5。LiteOS官方已經(jīng)適配過cortex M系列內核的單片機,因此移植過程非常簡單。
LiteOS有兩種移植方案:OS接管中斷和非接管中斷方式。接管中斷的方式,是由LiteOS創(chuàng)建很管理中斷,需要修改stm32啟動文件,移植比較復雜。STM32的中斷管理做的很好,用不著由LiteOS管理中斷,所以我們下邊的移植方案,都是非接管中斷的方式的。中斷的使用,跟在裸機工程時是一樣的。
在target_config.h 中將 LOSCFG_PLATFORM_HWI 宏定義為 NO,即為不接管中斷方式。該值默認為NO 。
移植的主要步驟如下:
1、添加內核文件
2、配置頭文件
3、移除systick和pendsv中斷
4、修改target_config.h
5、重定向printf函數(shù)(一般在裸機工程中就會實現(xiàn))
說明:內核運行過程中會通過串口打印一些錯誤信息。如果日志功能開啟、而又沒有重定向printf函數(shù)的話,則會導致日志打印出錯,程序異常卡死。之前我就是沒有重定向printf函數(shù),結果出了莫名其妙的問題,程序異常卡死在創(chuàng)建任務的地方。
下邊我們通過新建一個裸機工程,一步步講解如何進行移植。以下是詳細過程。
一、創(chuàng)建裸機工程
我們這次使用的是一個STM32F103C8T6的最小系統(tǒng)板,板載有三個LED、一個串口。LED連接引腳為(PB5PB6PB7),低電平點亮;串口為USART1(PA9,PA10),采用DMA+空閑中斷的方式接收數(shù)據(jù)。我們利用STM32CubeMX來生成裸機工程(STM32CubeMX的使用本文不詳細描述),設置如下:
1、引腳配置
配置PB5PB6PB7為推挽輸出方式;
配置PA9PA10為USART1復用功能;
配置PA13為SWDIO功能,PA14為SWCLK功能(下載及調試)
使能串行調試功能
2、時鐘配置
3、串口配置
4、生成代碼
勾選生成對應外設驅動的‘.c/.h’文件,生成代碼。
打開工程,加入LED開關狀態(tài)的宏定義和串口空閑中斷接收的代碼,具體如下(當然,如果你不使用DMA+空閑中斷的方式,也可以不進行下邊2中的修改,但是一定要重定向printf函數(shù)):
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、實現(xiàn)串口空閑中斷接收
在usart.h中加入如下代碼:
1 #define UART1_BUFF_SIZE 256 //串口接收緩存區(qū)長度
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庫函數(shù)printf到USART1
49 int fputc(int ch, FILE *f)
50 {
51 /* 發(fā)送一個字節(jié)數(shù)據(jù)到USART1 */
52 UART_SendByte(USART1, (uint8_t) ch);
53 return (ch);
54 }
55
56 ///重定向c庫函數(shù)scanf到USART1
57 int fgetc(FILE *f)
58 {
59 /* 等待串口1輸入數(shù)據(jù) */
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,并在串口中斷中調用該函數(shù):
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閃爍,同時打印字符串。
經(jīng)過上述操作,我們已經(jīng)完成了裸機工程的準備工作。
二、內核移植
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提供三套動態(tài)內存算法,位于kernel/base/mem目錄下,分別為bestfit、bestfit_little、tlsf,我們本次移植的是bestfit_little.可根據(jù)需求移植其他的算法。kernelasememmembox目錄下是 LiteOS 提供的靜態(tài)內存算法,與動態(tài)內存算法不沖突。
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
將這兩個中斷處理函數(shù)屏蔽掉。否則會出現(xiàn)如下編譯錯誤。
說明:liteos內核使用到了systick和pendsv這兩個中斷,并在內核代碼中有對應實現(xiàn)
6、修改target_config.h
OS_CONFIG/target_config.h 文件,該文件主要用于配置MCU驅動頭文件、RAM大小、內核功能等,需要根據(jù)自己的環(huán)境進行修改。
我們主要需要修改以下兩處:
MCU驅動頭文件
根據(jù)使用的MCU,包含對應的頭文件。
SRAM大小
根據(jù)使用的MCU芯片SRAM大小進行修改。
這里我們使用的是STM32F103C8T6,其SRAM為20KB。
不接管中斷
設置LOSCFG_PLATFORM_HWI 宏定義為 NO(該值默認為NO,一般無需修改,出于謹慎,移植過來還是要檢查下)
target_config.h 文件還有很多其他宏定義,主要是配置內核的功能。比如是否使用隊列、軟件定時器、是否使用時間片、信號量等。
經(jīng)過以上的操作,LiteOS的移植就完成了。點擊編譯。
7、創(chuàng)建一個任務
經(jīng)過前面的操作,移植工作就完成了,這里我們可以創(chuàng)建一個任務,使用LiteOS。在下邊的例子中,我們創(chuàng)建了兩個任務,一個任務按照2S的周期點亮LED1,另外一個任務按照400毫秒的周期點亮LED2。以下是代碼實現(xiàn):
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的樣例代碼,可參考。利用串口空閑中斷,可以很好的實現(xiàn)數(shù)據(jù)分幀)
LiteosPorting.rar
作者:llb90
HDC.Cloud華為開發(fā)者大會2020 即將于2020年2月11日-12日在深圳舉辦,是一線開發(fā)者學習實踐鯤鵬通用計算、昇騰AI計算、數(shù)據(jù)庫、區(qū)塊鏈、云原生、5G等ICT開放能力的最佳舞臺。
歡迎報名參會
總結
以上是生活随笔為你收集整理的【LiteOS】STM32F103-LiteOS移植教程(详细篇)【华为云技术分享】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 因为银行原因出现房贷逾期,怎么修复征信?
- 下一篇: 开源数据采集组件比较: scribe、c