基于STM32的uc/OS系统移植及用Saleae Logic 16抓取分析波形
文章目錄
- 一、關于uc/OS系統
- 1.操作系統與裸機的區別
- 2.uc/OS運行流程
- 二、詳細移植過程
- 1.STM32Cubex創建工程
- 2.為工程添加源碼
- 3.添加頭文件路徑
- 4.添加代碼
- 1)bsp.h和bsp.c
- 2)main.c
- 3)usart.c
- 4)修改其他代碼
- 5.其他設置
- 6.實際效果
- 三、分析引腳波形
- 1.Keil虛擬仿真邏輯儀
- 2.真實邏輯儀(SaleaeLogic16)
- 四、總結
- 五、參考資料
一、關于uc/OS系統
1.操作系統與裸機的區別
裸機運行的程序代碼,一般由一個main函數中的while死循環和各種中斷服務程序組成,平
時CPU執行while循環中的代碼,出現其他事件時,跳轉到中斷服務程序進行處理,沒有多任
務、線程的概念。而引人操作系統后,程序運行時可以把一個應用流程分割為多個任務,每個任務完成一部分工作,并且每個任務都可以寫成死循環。操作系統根據任務的優先級,通過調度器使CPU分時執行各個任務,保證每個任務都能夠得到運行。若調度方法優良,則可使各任務看起來是并行執行的,減少了CPU的空閑時間,提高了CPU的利用率。
2.uc/OS運行流程
在運行uC/OS系統的設備上,當程序執行時,首先會初始化系統任務管理所需要的各種鏈表等數據結構,接著根據應用程序需求創建任務,最后由調度器管理各個任務,而中斷可由操作系統使能和除能,若使能中斷則可以在其他任務運行時跳轉到中斷服務程序。
uc/OS工作流程圖
1)初始化所有全局變量、數據結構,創建最低優先級空閑任務OSTaskIde (如果使用了統計任務,也在此創建),創建6個空數據鏈表:
- 空任務控制塊鏈表
- 空事件控制塊鏈表
- 空隊列控制塊鏈表
- 空標志組鏈表
- 空內存控制塊鏈表
- 空閑定時器控制塊鏈表
2)至少創建一個任務。一般創建一個最高優先級別
TaskStart任務(建議), 任務調度后,在這個任務中再創建其他任務,初始化硬件并開中斷。
3)進入多任務管理階段,將就緒表中最高優先級任務的
棧指針加載到SP中,并強制中斷返回。
4) μC/OS 的任務調度工作。任務調度是內核的主要服務,是區分裸機和多任務系統的最大特點,好的調度策略能更好地發揮系統的效率。調度工作主要包括:查找就緒表中最高優先級任務和實現任務切換。而任務切換又分為兩種,分別為任務級的調度器OSSched和中斷級的調度器OSIntExt。
5)運行用戶任務,某些用戶任務會因為主動讓出CPU、延時、請求臨界資源或優先級不夠高而掛起,由調度器調度運行其他任務。
6) μC/OS的任務調度是靠周期時鐘中斷來實現的,每個時鐘節拍到來就會產生一次定時中斷,中斷后啟動調度器,運行就緒表中優先級最高的任務(非搶占型內核中斷后繼續運行被中斷任務)。即過一段時間就檢測是否有重要任務需要運行,若是就轉而運行更重要的任務,從而確保實時性(裸機程序就無法這樣做了)。在CM3芯片平臺上,這個周期時鐘中斷由SysTick提供。
二、詳細移植過程
將uc/OS-III移植到stm32上,構建3個任務:其中兩個task分別以1s和3s周期對LED等進行點亮-熄滅的控制;另外一個task以2s周期通過串口發送“hello uc/OS! 歡迎來到RTOS多任務環境!”。
1.STM32Cubex創建工程
新建project,選擇要用的芯片,這里選擇STM32F103C8,選中星號開啟項目。
配置調試接口為Serial Wire。
配置時鐘源,選擇Crystal/Ceramic Resonator。
將PC13、PA3設置為GPIO_OUTPUT模式,控制LED燈的亮滅,作為判斷是否移值成功的依據。
設置串口USART1為異步模式Asynchronous。
設置工程相關信息,并點擊GENERATE CODE。
2.為工程添加源碼
在工程相同目錄下新建下圖所示文件夾,提前把下的源碼整理進去,后面操作起來就會方便很多。
用Keil打開生成的工程,為項目添加如下圖所示文件夾。
點擊ADD FILES,選擇ALL FILES,將提前整理好的源碼添加進項目中的CPU文件夾。
點擊LIB,將下圖所示文件添加進項目中。
點擊PORT,將下圖所示文件夾加入項目中。
點擊Source,將下圖所示文件加入項目中。
點擊 CONFIG,將下圖所示文件添加進項目中。
點擊BSP,將下圖所示文件添加進項目中。
3.添加頭文件路徑
點擊魔法棒,選擇C/C++,點進Include Paths。
將下圖所示的頭文件路徑添加進項目中。
4.添加代碼
1)bsp.h和bsp.c
bsp.h
// bsp.h #ifndef __BSP_H__ #define __BSP_H__ #include "stm32f1xx_hal.h" void BSP_Init(void); #endifbsp.c
// bsp.c #include "includes.h" #define DWT_CR *(CPU_REG32 *)0xE0001000 #define DWT_CYCCNT *(CPU_REG32 *)0xE0001004 #define DEM_CR *(CPU_REG32 *)0xE000EDFC #define DBGMCU_CR *(CPU_REG32 *)0xE0042004 #define DEM_CR_TRCENA (1 << 24) #define DWT_CR_CYCCNTENA (1 << 0) CPU_INT32U BSP_CPU_ClkFreq (void) { return HAL_RCC_GetHCLKFreq(); } void BSP_Tick_Init(void) {CPU_INT32U cpu_clk_freq;CPU_INT32U cnts;cpu_clk_freq = BSP_CPU_ClkFreq();#if(OS_VERSION>=3000u)cnts = cpu_clk_freq/(CPU_INT32U)OSCfg_TickRate_Hz;#elsecnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;#endifOS_CPU_SysTickInit(cnts); } void BSP_Init(void) {BSP_Tick_Init();MX_GPIO_Init(); } #if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) void CPU_TS_TmrInit (void) {CPU_INT32U cpu_clk_freq_hz;DEM_CR |= (CPU_INT32U)DEM_CR_TRCENA; DWT_CYCCNT = (CPU_INT32U)0u;DWT_CR |= (CPU_INT32U)DWT_CR_CYCCNTENA;cpu_clk_freq_hz = BSP_CPU_ClkFreq();CPU_TS_TmrFreqSet(cpu_clk_freq_hz); } #endif #if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) CPU_TS_TMR CPU_TS_TmrRd (void) {return ((CPU_TS_TMR)DWT_CYCCNT); } #endif #if (CPU_CFG_TS_32_EN == DEF_ENABLED) CPU_INT64U CPU_TS32_to_uSec (CPU_TS32 ts_cnts) {CPU_INT64U ts_us;CPU_INT64U fclk_freq;fclk_freq = BSP_CPU_ClkFreq();ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);return (ts_us); } #endif #if (CPU_CFG_TS_64_EN == DEF_ENABLED) CPU_INT64U CPU_TS64_to_uSec (CPU_TS64 ts_cnts) {CPU_INT64U ts_us;CPU_INT64U fclk_freq;fclk_freq = BSP_CPU_ClkFreq();ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC); return (ts_us); } #endif2)main.c
/* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* 任務優先級 */ #define START_TASK_PRIO 3 #define LED0_TASK_PRIO 4 #define MSG_TASK_PRIO 5 #define LED1_TASK_PRIO 6/* 任務堆棧大小 */ #define START_STK_SIZE 96 #define LED0_STK_SIZE 64 #define MSG_STK_SIZE 64 #define LED1_STK_SIZE 64/* 任務棧 */ CPU_STK START_TASK_STK[START_STK_SIZE]; CPU_STK LED0_TASK_STK[LED0_STK_SIZE]; CPU_STK MSG_TASK_STK[MSG_STK_SIZE]; CPU_STK LED1_TASK_STK[LED1_STK_SIZE];/* 任務控制塊 */ OS_TCB StartTaskTCB; OS_TCB Led0TaskTCB; OS_TCB MsgTaskTCB; OS_TCB Led1TaskTCB;/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* 任務函數定義 */ void start_task(void *p_arg); static void AppTaskCreate(void); static void AppObjCreate(void); static void led_pc13(void *p_arg); static void send_msg(void *p_arg); static void led_pa3(void *p_arg); /* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /*** @brief System Clock Configuration* @retval None*/ void SystemClock_Config(void) {RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/**Initializes the CPU, AHB and APB busses clocks */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/**Initializes the CPU, AHB and APB busses clocks */RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();} }/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/ int main(void) {OS_ERR err;OSInit(&err);HAL_Init();SystemClock_Config();//MX_GPIO_Init(); 這個在BSP的初始化里也會初始化MX_USART1_UART_Init(); /* 創建任務 */OSTaskCreate((OS_TCB *)&StartTaskTCB, /* Create the start task */(CPU_CHAR *)"start task",(OS_TASK_PTR ) start_task,(void *) 0,(OS_PRIO ) START_TASK_PRIO,(CPU_STK *)&START_TASK_STK[0],(CPU_STK_SIZE) START_STK_SIZE/10,(CPU_STK_SIZE) START_STK_SIZE,(OS_MSG_QTY ) 0,(OS_TICK ) 0,(void *) 0,(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),(OS_ERR *)&err);/* 啟動多任務系統,控制權交給uC/OS-III */OSStart(&err); /* Start multitasking (i.e. give control to uC/OS-III). */}void start_task(void *p_arg) {OS_ERR err;CPU_SR_ALLOC();p_arg = p_arg;/* YangJie add 2021.05.20*/BSP_Init(); /* Initialize BSP functions *///CPU_Init();//Mem_Init(); /* Initialize Memory Management Module */#if OS_CFG_STAT_TASK_EN > 0uOSStatTaskCPUUsageInit(&err); //統計任務 #endif#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了測量中斷關閉時間CPU_IntDisMeasMaxCurReset(); #endif#if OS_CFG_SCHED_ROUND_ROBIN_EN //當使用時間片輪轉的時候//使能時間片輪轉調度功能,時間片長度為1個系統時鐘節拍,既1*5=5msOSSchedRoundRobinCfg(DEF_ENABLED,1,&err); #endif OS_CRITICAL_ENTER(); //進入臨界區/* 創建LED0任務 */OSTaskCreate((OS_TCB * )&Led0TaskTCB, (CPU_CHAR * )"led_pc13", (OS_TASK_PTR )led_pc13, (void * )0, (OS_PRIO )LED0_TASK_PRIO, (CPU_STK * )&LED0_TASK_STK[0], (CPU_STK_SIZE)LED0_STK_SIZE/10, (CPU_STK_SIZE)LED0_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,(OS_ERR * )&err); /* 創建LED1任務 */OSTaskCreate((OS_TCB * )&Led1TaskTCB, (CPU_CHAR * )"led_pa3", (OS_TASK_PTR )led_pa3, (void * )0, (OS_PRIO )LED1_TASK_PRIO, (CPU_STK * )&LED1_TASK_STK[0], (CPU_STK_SIZE)LED1_STK_SIZE/10, (CPU_STK_SIZE)LED1_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,(OS_ERR * )&err); /* 創建MSG任務 */OSTaskCreate((OS_TCB * )&MsgTaskTCB, (CPU_CHAR * )"send_msg", (OS_TASK_PTR )send_msg, (void * )0, (OS_PRIO )MSG_TASK_PRIO, (CPU_STK * )&MSG_TASK_STK[0], (CPU_STK_SIZE)MSG_STK_SIZE/10, (CPU_STK_SIZE)MSG_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err);OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //掛起開始任務 OS_CRITICAL_EXIT(); //進入臨界區 } /*** 函數功能: 啟動任務函數體。* 輸入參數: p_arg 是在創建該任務時傳遞的形參* 返 回 值: 無* 說 明:無*/ static void led_pc13 (void *p_arg) {OS_ERR err;(void)p_arg;BSP_Init(); /* Initialize BSP functions */CPU_Init();Mem_Init(); /* Initialize Memory Management Module */#if OS_CFG_STAT_TASK_EN > 0uOSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */ #endifCPU_IntDisMeasMaxCurReset();AppTaskCreate(); /* Create Application Tasks */AppObjCreate(); /* Create Application Objects */while (DEF_TRUE){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);OSTimeDlyHMSM(0, 0, 1, 0,OS_OPT_TIME_HMSM_STRICT,&err);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);OSTimeDlyHMSM(0, 0, 1, 0,OS_OPT_TIME_HMSM_STRICT,&err);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */ }static void led_pa3 (void *p_arg) {OS_ERR err;(void)p_arg;BSP_Init(); /* Initialize BSP functions */CPU_Init();Mem_Init(); /* Initialize Memory Management Module */#if OS_CFG_STAT_TASK_EN > 0uOSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */ #endifCPU_IntDisMeasMaxCurReset();AppTaskCreate(); /* Create Application Tasks */AppObjCreate(); /* Create Application Objects */while (DEF_TRUE){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */ }static void send_msg (void *p_arg) {OS_ERR err;(void)p_arg;BSP_Init(); /* Initialize BSP functions */CPU_Init();Mem_Init(); /* Initialize Memory Management Module */#if OS_CFG_STAT_TASK_EN > 0uOSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */ #endifCPU_IntDisMeasMaxCurReset();AppTaskCreate(); /* Create Application Tasks */AppObjCreate(); /* Create Application Objects */while (DEF_TRUE){printf("hello uc/OS \r\n");OSTimeDlyHMSM(0, 0, 2, 0,OS_OPT_TIME_HMSM_STRICT,&err);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */ }/* USER CODE BEGIN 4 */ /*** 函數功能: 創建應用任務* 輸入參數: p_arg 是在創建該任務時傳遞的形參* 返 回 值: 無* 說 明:無*/ static void AppTaskCreate (void) {}/*** 函數功能: uCOSIII內核對象創建* 輸入參數: 無* 返 回 值: 無* 說 明:無*/ static void AppObjCreate (void) {}/* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/ void Error_Handler(void) {/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state *//* USER CODE END Error_Handler_Debug */ }#ifdef USE_FULL_ASSERT /*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/3)usart.c
添加以下代碼對printf函數進行重定向。
int fputc(int ch,FILE *f){HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);return ch; }4)修改其他代碼
找到Application/MDK-ARM下的startup_stm32f103xb.s文件,將PendSV_Handler和SysTick_Handler改為OS_CPU_PendSVHandler和OS_CPU_SysTickHandler。
找到CONFIG下的app_cfg.h文件,將DEF_ENABLED改為DIABLED。
將#define APP_TRACE BSP_Ser_Printf 改為#define APP _TRACE (void)。
在該目錄下找到文件includes.h,添加包含兩個頭文件gpio.h、app_cfg.h。
將#include < stm32f10x lib.h> 改為#include "stm32f1xx hal.h"
在該目錄下找到lib_cfg.h,修改堆空間的大小,改成5k,因為STM32F103C8RAM太小了,不修改編譯會報錯。
5.其他設置
如下圖所示修改設置,這個為Initialization File添加在同一目錄下debug.ini文件的路徑,這里為待會使用邏輯分析儀抓取引腳波形做準備,如果不添加待會仿真時按下全速運行會自動停止并報錯沒有讀取權限。
debug.ini
map 0x40000000, 0x40007FFF read write // APB1 map 0x40010000, 0x400157FF read write // APB2 map 0x40020000, 0x4007FFFF read write // AHB1 map 0x50000000, 0x50060BFF read write // AHB2 map 0x60000000, 0x60000FFF read write // AHB3 map 0xE0000000, 0xE00FFFFF read write // CORTEX-M4 internal peripherals
IRAM也設置的大一些。
6.實際效果
將生成的hex文件燒錄進芯片中,然后將boot0置0,看到實際效果如下:兩個LED按不同的周期點亮、熄滅,串口調試助手上輸出“hello uc/OS! 歡迎來到RTOS多任務環境!”。
三、分析引腳波形
分別使用Keil虛擬仿真邏輯儀和真實邏輯儀(SaleaeLogic16)抓取LED輸出電平和串口通信的波形,進行協議分析。
1.Keil虛擬仿真邏輯儀
進入Keil,按Ctrl+F5進入仿真調試,打開邏輯分析儀。
點擊左上角的SET UP,添加要觀察的引腳PA3、PC13和串口,輸入時輸入PORTA.3,然后回車就會自動生成下圖所示的表示形式,注意Display Type要設置成Bit形式。
按F5全速運行,可以看到下圖所示的波形:
一個網格是0.5S,兩只LED燈一只以1S的周期,一只以3秒的周期在高低電平轉換,串口發送數據的周期是2S,在邏輯分析儀上不好詳細分析串口的波形,但在SaleaeLogic16的軟件上就能比較直觀地分析。
2.真實邏輯儀(SaleaeLogic16)
想要詳細了解SaleaeLogic16可以看我這篇博客用 Saleae Logic 16 示波器測量并分析 I2C、SPI、串口的信號
這里使用SaleaeLogic16的CH0、CH1、CH2通道,分別接STM32的PA3、PC13和PA9,再通過USB將電源接到電腦上,邏輯分析儀的Ground線接到STM32的GND引腳,注意高、低電平最好來自同一個電源,否則可能出現嚴重的毛刺,影響觀測效果。
然后在軟件界面上點擊Analyzers右邊的加號,都選擇Async Serial,設置對應的通道號,點擊左上角的Start即可抓取在系統運作時的引腳波形。
抓取的波形如下圖所示,可以看到PA3、PC13分別以接近1S、3S在高低電平轉換,串口發送數據的周期是接近2S。
將高低電平跳變處放大仔細看一下串口的波形。
Saleae Logic 16軟件的有數據解析功能,軟件能夠解析出框選中的時序波形00001011011對應"h",其中:
第1個bit 0:數據起始位。
中間8個bit 00010110:有效數據位。從左往右依次為高位,即0110 1000對應"h"的ASCII碼0x68。
第9個bit 1:停止位。
第10個bit 1:空閑位。
根據上述內容,框選中的時序波形01010011011有效數據位10100110從左到右依次為高位,即0110 0101對應"e"的ASCII碼0x65,所觀察到的內容與串口通信的時序波形相符合。
四、總結
將uc/OS-III移植到stm32后,程序運行時可以把一個應用流程分割為多個任務,操作系統根據任務的優先級,通過調度器使CPU分時執行各個任務,使各任務看起來是并行執行的,減少了CPU的空閑時間,提高了CPU的利用率。另外,真實邏輯分析儀使用起來真的比Keil的邏輯分析儀使用起來方便許多,通過對這些圖形化的時序波形的觀察,能迅速定位到實際中軟件或硬件的錯誤并排除故障。
五、參考資料
Saleae Logic 16安裝文件及上手指南
提取碼:650v
STM32F103C8T6移植uC/OS-III基于HAL庫超完整詳細過程
源碼
提取碼:1234
總結
以上是生活随笔為你收集整理的基于STM32的uc/OS系统移植及用Saleae Logic 16抓取分析波形的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mac安装kafka
- 下一篇: Dell R720\R720xd\R73