一、实验要求
1.学习嵌入式实时操作系统(RTOS),以uc/OS-III为例,将其移植到stm32F103上,构建至少3个任务(task):其中两个task分别以1s和3s周期对LED等进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。记录详细的移植过程。
2.在上述实验中,在掌握Keil的仿真调试代码功能之外,也学习使用仪器对代码运行进行故障排查和功能调测。
(1)练习使用示波器去观察LED输出电平和串口通信的波形,分析故障;
(2)分别使用Keil虚拟仿真逻辑仪和真实逻辑仪(SaleaeLogic16)抓取LED输出电平和串口通信的波形,进行协议分析。
二、实验过程及结果
(一)移植uc/OS-III
1. 下载uC/OS-III源码
官网下载:http://micrium.com/downloadcenter/
云盘下载:https://pan.baidu.com/s/1Btj7foEXdXjjJWoZQsN-OQ 提取码:mleh
2. 建立STM32HAL库
选择STM32F103C8
芯片,修改RCC
中的HSE
为Crystal/Ceramic Resonator
点击System Core
中的SYS
,在Debug
处选择Serial Wire
选择串口USART1
,点击Mode
,选择Asynchronous
设置引脚PA1
、PB15
为GPIO_Output
点击Clock Configuration
,选择HSE
和PLLCLK
,设置HCLK
为72MHz
设置项目名称和路径,修改Toolchain/IDE
为MDK-ARM
,在Code Generator
中勾选
3. 复制uC/OS-III文件
在生成的keil工程文件夹中新建一个uCOS
文件夹
将下载源文件下的uC-CPU
、uC-LIB
、uCOS-III
复制到此
在Core
中的Src
文件夹下新建一个OS
文件夹
将Micrium\Software\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III
下的app.c
、 app_cfg.h
、 cpu_cfg.h
、 includes.h
、 lib_cfg.h
、 os_app_hooks.c
、os_app_hook.h
、os_cfg.h
、os_cfg_app.h
文件复制到OS文件夹
在OS
文件夹中新建三个空白文件 bsp.c
、bsp.h
、app.h
4. 添加工程组件及路径
打开工程, 添加六个新组:bsp
、uCOSIII_CPU
、uCOSIII_LIB
、uCOSIII_Ports
、uCOSIII_Source
、OS_cfg
将app.c
添加进Application/User/Core
组,将Core/Src/OS
中的bsp.c
、bsp.h
添加进bsp
组
将uCOS/uC-CPU
中的cpu_core.c
、cpu_core.h
、cpu_def.h
和uCOS/uC-CPU/ARM-Cortex-M3/RealView
中的cpu.h
、cpu_c.c
、cpu_a.asm
添加进uCOSIII_CPU
组
将uCOS/uC-LIB
中的9个文件和uCOS/uC-LIB/Ports/ARM-Cortex-M3/Realview
中的lib_mem_a.asm
添加到uCOSIII-LIB
组
将uCOS/uCOS-III/Ports/ARM-Cortex-M3/Generic/RealView
中的3个文件添加到uCOSIII_Ports
组
将uCOS/ucos-III/Source
中的20个文件添加到uCOSIII_Source
组
将Core/Src/OS
中的8个文件添加到OS_cfg
组
点击魔法棒
,选择C/C++
添加文件路径
点击魔法棒
,选择Target
配置相关参数
5. 修改文件内容
(1)startup_stm32f103xb.s启动文件
PendSV_Handler -> OS_CPU_PendSVHandler
Systick_Handler -> OS_CPU_SysTickHandler
(2)app_cfg.h头文件
//修改前
#define APP_CFG_SERIAL_EN DEF_ENABLED
#define APP_TRACE BSP_Ser_Printf
//修改后
#define APP_CFG_SERIAL_EN DEF_DISABLED
#define APP_TRACE (void)
(3)includes.h头文件
//添加相关头文件
#include "gpio.h"
#include "app_cfg.h"
#include "app.h"
//添加HAL库
#include <stm32f10x_lib.h>//修改前
#include "stm32f1xx_hal.h"//修改后
(4)bsp.c、bsp.h文件
bsp.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;
#else
cnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;
#endif
OS_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; /* Enable Cortex-M3's DWT CYCCNT reg. */
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);
}
#endif
bsp.h
// bsp.h
#ifndef __BSP_H__
#define __BSP_H__
#include "stm32f1xx_hal.h"
void BSP_Init(void);
#endif
(5)app.c、app.h文件
app.c
#include "includes.h"
app.h
// app.h
#ifndef __APP_H__
#define __APP_H__
#include <includes.h>
#endif /* __APP_H__ */
(6)lib_cfg.h文件
STM32F103C8
的RAM
总共20K
//修改前
#define LIB_MEM_CFG_HEAP_SIZE 27u * 1024u
//修改后
#define LIB_MEM_CFG_HEAP_SIZE 5u * 1024u //宏定义设置堆空间的大小
(7)usart.c文件
#include "stdio.h"
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
return ch;
}
(8)main.c文件
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "usart.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <includes.h>
#include "stm32f1xx_hal.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* 任务优先级 */
#define START_TASK_PRIO 3
#define LED0_TASK_PRIO 4
#define LED1_TASK_PRIO 4
#define MSG_TASK_PRIO 5
/* 任务堆栈大小 */
#define START_STK_SIZE 64
#define LED0_STK_SIZE 64
#define LED1_STK_SIZE 64
#define MSG_STK_SIZE 64//任务堆大小过大会报错,改小一点
/* 任务栈 */
CPU_STK START_TASK_STK[START_STK_SIZE];
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
CPU_STK MSG_TASK_STK[MSG_STK_SIZE];
/* 任务控制块 */
OS_TCB StartTaskTCB;
OS_TCB Led0TaskTCB;
OS_TCB Led1TaskTCB;
OS_TCB MsgTaskTCB;
/* 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_PA1(void *p_arg);
static void LED_PB15(void *p_arg);
static void send_msg(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 > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //进入临界区
/* 创建LED0任务 */
OSTaskCreate((OS_TCB * )&Led0TaskTCB,
(CPU_CHAR * )"LED_PA1",
(OS_TASK_PTR )LED_PA1,
(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_PB15",
(OS_TASK_PTR )LED_PB15,
(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_PA1 (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 > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
static void LED_PB15 (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 > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 2, 500,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 > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
printf("hello uc/OS! 欢迎来到RTOS多任务环境!\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****/
6.硬件操作
将USB-TTL
的GND
和3.3V
接入STM32系统板
的GND
和3.3V
,然后TXD
和RXD
分别接入A10
和A9
引脚。
接入后按照程序中GPIOx
的引脚接上LED灯
,且最小核心板要利用跳线帽实现boot0置1
,boot1置0
。
打开 mcuisp
软件,开始烧录。
7.实际效果
uc/OS系统移植

(二)信号分析
1. Keil虚拟仿真逻辑仪
(1)设置参数
出现错误:error 65: access violation at 0x40023800 : no 'read' permission
根本原因:map地址空间权限映射有问题。部分地址空间没有读写的权限,程序不能自动运行。
解决方法:
① 在工程文件中新建debug,ini
文件,添加map地址权限映射代码
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
② 在Debug
页添加ini
文件
(2)调试波形
打开逻辑分析仪和Serial Windows
中的UART #1
窗口,添加端口并将Display Type
设置为Bit
。
观察波形可知:
PA1
引脚灯周期为1秒,PB15
引脚灯周期为3秒,USART1
每2秒输出一次
2. SaleaeLogic16真实逻辑仪
(1)下载软件
云盘下载:https://pan.baidu.com/s/1ycY6O-N0oK-Wj74uQRn0fg 提取码:0526
安装软件时驱动程序注册到系统,当插入SALEAE 16
逻辑分析仪后自动安装驱动。
(2)介绍软件
软件界面基本是左中右的布局,左边主要是采集和显示设置,右边是分析和解析设置,中间是波形显示区域。
无逻辑分析仪的时候显示 Disconnected
,左边是8个通道,使用的时候根据硬件连接选择对应通过进行配置,中间部分是显示逻辑波形的区域,右边是添加一些协议数据格式,数据分析区域。
点击 Start Simulation
,可在波形区域模拟显示出一些软件生成的数据,如果设置了解析,可以根据所设置的协议,生成一些符合协议解析要求的模拟数值。
解析设置方法:
① 默认的演示模式是 8 通道的,可设置成 16 通道的
② 点击左上角的符号,弹出选择演示的设备
③ 选择 LOGIC16
,界面出现 SALEAE16
的设置
④ 通道设置:波形幅值比例,进行修改不同的比例
⑤ 触发方式选择:上升沿、下降沿、双边沿,主要跟协议信号有关系
⑥ Annotations
选项:增加测量电平时间的组数,类似示波器测量时间的标线
⑦ Analyzers
选项:包含很多协议,常用的串口、SPI、IIC、CAN 等都可进行测量
(3)观测波形
① 线路连接
逻辑分析仪 | STM32F103C8T6 |
---|---|
CH0 | PA1 |
CH1 | PB15 |
CH2 | PA9 |
GND | GND |
连接逻辑分析仪后,左上角的采集按钮由以前的 START STMULTAR变成START。
② 波形采集
③ 波形分析
PA1
引脚输出电平周期为 1s,PB15
引脚输出电平周期为 3s,USART1
串口输出周期为 2s,与程序设计的周期相同。
④ 协议分析
使用UART通信协议进行一个字节数据的传输就是在信号线上产生八个高低电平的组合。数据传送速率用波特率来表示,即每秒钟传送的二进制位数。以9600,8-N-1(8个数据位,没有校验位,1位停止位)为例,串口波特率为9600,1bit的传输时间大约为1/9600=104us
,传送一个数据实际是传送10个比特(开始位-8个数据位-停止位),一个bytes的传输速率实际是9600*8/10=7680bps
。
UART 使用异步串行通信。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔多少是不固定的,但在同一个字符中的两个相邻位间的时间间隔是固定的。
串口通信协议数据传送时,每一个字符为10位(1个起始位,7个数据位,1个校验位,1个结束位):
起始位—先发出一个逻辑“ 0 ”信号,表示传输字符的开始
数据位—可以是 5~8 位逻辑“ 0 ”或“ 1 ”
校验位—数据位加上这一位后,使得“ 1 ”的位数应为偶数(偶校验)或奇数(奇校验)
停止位—它是一个字符数据的结束标志。可以是 1 位、1.5 位、2 位的高电平
空闲位—处于逻辑“ 1 ”状态,表示当前线路上没有资料传送
三、实验总结
在本次实验过程中,我学会了如何将uc/OS-III移植到stm32F103上并构建相关任务,学会了如何使用Keil虚拟仿真逻辑仪和SaleaeLogic16真实逻辑仪抓取LED输出电平和串口通信的波形,进行协议分析。其实,移植uc/OS-III到stm32F103上的过程并不难,但在添加工程组件和修改文件内容时需特别小心仔细,否则移植会不成功。在使用Keil虚拟仿真逻辑仪进行仿真调试的过程中,调试界面的左下角出现了error 65: access violation at 0x40023800 : no 'read' permission
的错误,最后通过查找此错误的相关资料,引入debug.ini
文件,才成功调试出波形。在使用SaleaeLogic16真实逻辑仪抓取LED输出电平和串口通信的波形时,出现了framing error
,通过查找资料我发现可能是因为波特率设置的问题,在最开始抓取波形时我并没有修改它的默认波特率9600,应该将波特率设置为115200的。
四、参考资料
1、uart通信协议详解
2、keil仿真和使用示波器调波形
3、STM32F103C8移植uCOSIII(HAL库)
4、STM32F103C8T6移植uCOS基于HAL库
5、STM32F103C8T6移植uC/OS-III基于HAL库超完整详细过程