…
Keil 标准库 创建STM32F103工程 首先创建一个项目目录:
STM32F103RBT
Project
User
Device
Driver
打开Keil,选择上方的Project
->New uVersion Project
,在Project目录下创建工程文件。在打开的面板左侧选择版型,这里选择STM32F1 Series
->STM32F103
->STM32F103C8
。
下一步会弹出Manage Run-Time Environment
,这里是选择外设的地方。 这里选择需要的库:
CMSIS
Device
DMA
GPIO
Startup
StdPeriph Drivers
ADC
Framework
RCC
DMA
EXIT
GPIO
DGBMCU
TIM
SPI
USART
创建完成后打开Options for Target
,在C/C++
选项卡的Define
项填写:
1 STM32F10X_MD,USE_STDPERIPH_DRIVER
前一项表示板子的类型,由与是C8板,因此是MD型;如果是Z系列或V系列,则为HD型。 后一项表示使用标准库。
在Include Paths
中填写,这里是.h
文件的搜索路径
1 ..\Device;..\Driver;..\User;..\Project;..\UCOS;..\UCOS\core;..\UCOS\cpu;..\UCOS\lib
在Debug
选项卡右侧的Use
中选择J-LINK/J-TRACE Cortex
。完成后点击Settings
,选择Flash Download
选项卡,删除Programming Algorithm
中的项目,点击ADD
,添加第一项。另外,在Debug
选项卡可以选择Port
,可以是Jtag
或SW
,可根据下载器选择。完成后确定返回。
这里开始创建一个main.c
和一个main.h
文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include "main.h" int main (void ) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA, ENABLE); gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Pin = GPIO_Pin_2; GPIO_Init (GPIOA, &gpio); while (1 ) { GPIO_ResetBits (GPIOA, GPIO_Pin_2); } }
1 2 3 4 5 6 7 8 #ifndef MAIN_H #define MAIN_H #include "stm32f10x.h" #endif
之后打开Manage Project Items
按钮,编辑项目目录。这可以按照项目目录创建Groups
,并在对应的Files
中添加相应.c
文件。
最后编译下载查看效果。
STM32F4 宏定义 USE_STDPERIPH_DRIVER 使用标准库 STM32F40_41xxx 选择芯片型号 ARM_MATH_CM4 使用ARM MATH
库,调用片内DSP
修改时钟源 由于STM32F4默认HSE为25MHz,而一般的生产厂家配置的是8MHz的晶振,因此默认情况下,程序跑不到168MHz,需要更改默认配置。
打开system_stm32f4xx.c
,配置如下选项:
找到PLL Parameters
下的#if defined(STM32F40_41xxx)
配置PLL_M
由25
改为8
配置PLL_N
保持为336
配置PLL_P
保持为2
打开stm32f4xx.h
中定义HSE_VALUE
1 #define HSE_VALUE 8000000
USB USB 协议 USB 协议 设备枚举
物理层 物理层上,USB协议采用差分信号,使用两个数据线D+
和D-
。
(VOH>2.8V, VOL<0.3V) 信号 1, J状态:D+>VOH, D-<VOL
(D+ = 1, D- = 0)
信号 0, K状态:D+<VOL, D->VOH
(D+ = 0, D- = 1)
Reset:D+<VOL, D-<VOL, time>10ms
(D+ = 0, D- = 0)
SE0状态:(D+ = 0, D- = 1)
IDLE状态:空闲状态 Suspend:J状态保存3ms以上 SYNC:3个K J切换后跟随两位时间的K状态 Resume:20ms的K状态+低速EOP SOP:从IDLE切换到K状态 EOP:持续2位时间的SE0信号,后跟随一位时间的J状态 Keep alive:低速EOP信号
主机通过设备在D+或D-上的1.5K上拉来检测设备的连接和断开事件,并由此判别设备的速度。
低速设备:D-
被上拉。 高速,全速设备:D+
被上拉。主机先把高速设备检测为全速设备,然后再通过“Chirp序列”的总线握手机制来识别高速和全速设备
USB 连接与断开 在连接时,当主机检测到某一个数据线电平拉高并保持了一段时间,就认为有设备连上来了。主机必需在驱动SE0状态以复位设备之前,立刻采样总线状态来判断设备的速度。
在断开时,HOST端的D+和D-数据线上的下拉电阻起作用,使得二者都在低电平,主机端看来就是个SE0状态。当数据线上的SE0状态持续一段时间了,就被主机认为是断开状态。
数据传输 USB采用NRZI(非归零编码)对发送的数据包进行编码: 输入数据0, 编码成“电平翻转” 输入数据1, 编码成“电平不变”
数据流中每6个连续的“1”,就要插入1个“0”,从而保证编码。接收方赋值解码NRZI码流,然后识别出填充位,并丢弃它们。
USB 传输 传输又分为四种类型:批量传输、等时(同步)传输、中断传输、控制传输。USB传输数据先发数据低位再发高位数据。
一个传输有多个事务组成,一个事务由2或3个包组成。
包 Packet分四大类: 命令 (Token) 、Packet 帧首 (Start of Frame) 、Packet 数据 (Data) 、Packet 握手 (Handshake)
包的组成:
SOP
SYNC
Packet Content
PID:PID47是PID04的取反,用来校验PID。
地址:设备地址和端点地址,设备地址7位,端点地址4位。
帧号:11位,主机发出一个帧,帧号+1。达到7FFH时重新计数。
数据:长度从0到1024Byte不等。
CRC
EOP
PID 类型
PID 名称
Package 种类
Token 令牌
OUT/INT/SETUP/SOF
令牌包,帧首包
Data 数据
DATA0/DATA1/DATA2/MDATA
数据包
Handshake 握手
ACK/NAK/STALL/NYET
握手包
Special 特殊
PRE/ERR/SPLIT/PING
无
令牌包:用来启动一次USB传输。没有帧号和数据部分。
输出(OUT)令牌包:用来通知设备将要输出一个数据包 输入(IN)令牌包:用来通知设备返回一个数据包 建立(SETUP)令牌包:只用在控制传输中,和输出令牌包作用一样,也是通知设备将要输出一个数据包,两者区别在于: SETUP令牌包后只使用DATA0数据包,且只能发送到设备的控制端点,并且设备必须要接收,而OUT令牌包没有这些限制
帧起始包:在每帧(或微帧)开始时发送,以广播的形式发送,所有USB全速设备和高速设备都可以接收到SOF包。没有地址和数据部分。
数据包:没有地址和帧号。
握手包:只有PID部分。
事务 事务可分为3类: Setup transaction:主机用来向设备发送控制命令 Data IN transaction:主机用来从设备读取数据 Data OUT transaction:主机用来向设备发送数据
包组成: Token packet:总是由主机发出 Data packet:包含此次transaction的数据负载
传输 四种传输类型:
批量(大容量数据)传输(Bulk Transfers): 非周期性,突发 大容量数据的通信,数据可以占用任意带宽,并容忍延迟 。如USB打印机、扫描仪、大容量储存设备等
中断传输(Interrupt Transfers): 周期性,低频率 允许有限延迟的通信 如人机接口设备(HID)中的鼠标、键盘、轨迹球等
等时(同步)传输(Isochronous Transfers): 周期性 持续性的传输,用于传输与时效相关的信息,并且在数据中保存时间戳的信息 ,如音频视频设备
控制传输(Control Transfers): 非周期性,突发 用于命令和状态的传输
批量传输 批量输出事务
(1)主机先发出一个OUT令牌包(包含设备地址,端点号)
(2)然后再发送一个DATA包,这时地址和端点匹配的设备就会收下这个数据包,主机切换到接收模式,等待设备返回握手包
(3)设备解码令牌包,数据包都准确无误,并且有足够的缓冲区来保存数据后就会使用ACK/NYET握手包来应答主机(只有高速模式才有NYET握手包,他表示本次数据成功接收,但是没有能力接收下一次传输),如果没有足够的缓冲区来保存数据,就返回NAC,告诉主机目前没有缓冲区可用,主机会在稍后时间重新该批量传输事务。如果设备检查到数据正确,但端点处于挂起状态,返回STALL。如果检测到有错误(如校验错误,位填充错误),则不做任何响应,让主机等待超时。
批量输入事务
(1)主机首先发送一个IN令牌包(包含设备地址,端点号)
(2)主机切换到接收数据状态等待设备返回数据。如果设备检测到错误,不做任何响应,主机等待超时。如果此时有地址和端点匹配的设备,并且没有检测到错误,则该设备作出反应:设备有数据需要返回,就将一个数据包放在总线上;如果没有数据需要返回,设备返回NAK响应主机;如果该端点处于挂起状态,设备返回STALL。如果主机收到设备发送的数据包并解码正确后,使用ACK握手包应答设备。如果主机检测到错误,则不做任何响应,设备会检测到超时。注意:USB协议规定,不允许主机使用NAK来拒绝接收数据包。主机收到NAK,知道设备暂时没有数据返回,主机会在稍后时间重新该批量输入事务。
PING令牌包
不发送数据,直到等待设备的握手包。
中断传输 中断传输是一种保证查询频率的传输。中断端点在端点描述符中要报告它的查询间隔,主机会保证在小于这个时间间隔的范围内安排一次传输。
等时传输 等时(同步)传输用在数据量大、对实时性要求高的场合,如音频设备,视频设备等,这些设备对数据的延迟很敏感。对于音频或视频设备数据的100%正确性要求不高,少量的数据错误是可以容忍的,主要是保证数据不能停顿,所以等时传输是不保证数据100%正确的。当数据错误时,不再重传操作。因此等时传输没有应答包,数据是否正确,由数据的CRC校验来确认。
控制传输 控制传输可分为三个过程: (1)建立过程 (2)数据过程(可选) (3)状态过程
每个USB设备都必须有控制端点,支持控制传输来进行命令和状态的传输。USB主机驱动将通过控制传输与USB设备的控制端点通信,完成USB设备的枚举和配置
控制传输是双向的传输,必须有IN和OUT两个方向上的特定端点号的控制端点来完成两个方向上的控制传输
USB 标准请求 标准请求结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 u8 bmRequestType; u8 bRequest; u16 wValue; u16 wIndex; u16 wLength;
请求代码bRequest
枚举:
1 2 3 4 5 6 7 8 9 10 11 GET_STATUS = 0 , CLEAR_FEATURE = 1 , SET_FEATURE = 3 , SET_ADDRESS = 5 , GET_DESCRIPTOR = 6 , SET_DESCRIPTOR = 7 , GET_CONFIGURATION = 8 , SET_CONFIGURATION = 9 , GET_INTERFACE = 10 , SET_INTERFACE = 11 , SYNCH_FRAME = 12
设备枚举和描述符 当一个USB设备插入主机后,会有以下活动:
供电
复位
获取设备描述符前8 Bytes
复位
分配地址
获取设备描述符
获取Configuration Descriptor
获取String Descriptor
配置
配置描述符:
1 2 3 4 5 6 7 8 9 10 11 12 13 struct usb_config_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 wTotalLength; __u8 bNumInterfaces; __u8 bConfigurationValue; __u8 iConfiguration; __u8 bmAttributes; __u8 bMaxPower; } __attribute__ ((packed)); #define USB_DT_CONFIG_SIZE 9
接口描述符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct usb_interface_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bInterfaceNumber; __u8 bAlternateSetting; __u8 bNumEndpoints; __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; __u8 iInterface; } __attribute__ ((packed)); #define USB_DT_INTERFACE_SIZE 9
类描述符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct usb_device_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 bcdUSB; __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; __u8 bMaxPacketSize0; __le16 idVendor; __le16 idProduct; __le16 bcdDevice; __u8 iManufacturer; __u8 iProduct; __u8 iSerialNumber; __u8 bNumConfigurations; } __attribute__ ((packed)); #define USB_DT_DEVICE_SIZE 18
端点描述符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct usb_endpoint_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bEndpointAddress; __u8 bmAttributes; __le16 wMaxPacketSize; __u8 bInterval; __u8 bRefresh; __u8 bSynchAddress; } __attribute__ ((packed)); #define USB_DT_ENDPOINT_SIZE 7 #define USB_DT_ENDPOINT_AUDIO_SIZE 9
字符串描述符:
1 2 3 4 5 6 struct usb_string_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 wData[1 ]; } __attribute__ ((packed));
在STM32的代码中,USB配置描述符,接口描述符,端点描述符这3个被整合为一个USB描述符,然后还是叫做USB配置描述符。例如(使用USB复合设备):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 const uint8_t Composite_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] ={ 配置描述符 IAD描述符 接口1 描述符 类描述符 端点描述符 接口2 描述符 类描述符 端点描述符 端点描述符 IAD描述符 接口描述符 类描述符 端点描述符 ... } const uint8_t Composite_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] ={ 0x09 , USB_CONFIGURATION_DESCRIPTOR_TYPE, CUSTOMHID_SIZ_CONFIG_DESC, 0x00 , 0x03 , 0x01 , 0x00 , 0xC0 , 0x32 , 0x08 , 0x0B , 0x00 , 0x01 , 0x03 , 0x00 , 0x01 , 0x00 , 0x09 , USB_INTERFACE_DESCRIPTOR_TYPE, 0x00 , 0x00 , 0x02 , 0x03 , 0x01 , 0x01 , 0 , 0x09 , HID_DESCRIPTOR_TYPE, 0x10 , 0x01 , 0x00 , 0x01 , 0x22 , CUSTOMHID_SIZ_REPORT_DESC, 0x00 , 0x07 , USB_ENDPOINT_DESCRIPTOR_TYPE, 0x84 , 0x03 , 0x08 , 0x00 , 0x20 , 0x07 , USB_ENDPOINT_DESCRIPTOR_TYPE, 0x04 , 0x03 , 0x01 , 0x00 , 0x20 , 0x08 , 0x0B , 0x01 , 0x02 , 0x02 , 0x02 , 0x01 , 0x00 , 0x09 , USB_INTERFACE_DESCRIPTOR_TYPE, 0x01 , 0x00 , 0x01 , 0x02 , 0x02 , 0x01 , 0x00 , 0x05 , 0x24 , 0x00 , 0x10 , 0x01 , 0x05 , 0x24 , 0x01 , 0x00 , 0x01 , 0x04 , 0x24 , 0x02 , 0x02 , 0x05 , 0x24 , 0x06 , 0x00 , 0x01 , 0x07 , USB_ENDPOINT_DESCRIPTOR_TYPE, 0x82 , 0x03 , VIRTUAL_COM_PORT_INT_SIZE, 0x00 , 0xFF , 0x09 , USB_INTERFACE_DESCRIPTOR_TYPE, 0x02 , 0x00 , 0x02 , 0x0A , 0x00 , 0x00 , 0x00 , 0x07 , USB_ENDPOINT_DESCRIPTOR_TYPE, 0x03 , 0x02 , VIRTUAL_COM_PORT_DATA_SIZE, 0x00 , 0x00 , 0x07 , USB_ENDPOINT_DESCRIPTOR_TYPE, 0x81 , 0x02 , VIRTUAL_COM_PORT_DATA_SIZE, 0x00 , 0x00 };
另外,对于HID设备还要有报告描述符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] = { 0x05 , 0x01 , 0x09 , 0x06 , 0xa1 , 0x01 , 0x05 , 0x07 , 0x19 , 0xe0 , 0x29 , 0xe7 , 0x15 , 0x00 , 0x25 , 0x01 , 0x95 , 0x08 , 0x75 , 0x01 , 0x81 , 0x02 , 0x95 , 0x01 , 0x75 , 0x08 , 0x81 , 0x03 , 0x95 , 0x06 , 0x75 , 0x08 , 0x25 , 0xFF , 0x19 , 0x00 , 0x29 , 0x65 , 0x81 , 0x00 , 0x25 , 0x01 , 0x95 , 0x02 , 0x75 , 0x01 , 0x05 , 0x08 , 0x19 , 0x01 , 0x29 , 0x02 , 0x91 , 0x02 , 0x95 , 0x01 , 0x75 , 0x06 , 0x91 , 0x03 , 0xc0 };
常见 USB 设备类 音频类(Audio) 通信设备类(CDC) 设备固件升级类(DFU) 人机接口类(HID) 大容量存储设备类(Mass Storage)
USB 代码库结构 usb_regs.c:操作USB控制寄存器。 usb_init.c:初始化USB控制器。 usb_int.c:处理中断。CTR_LP:负责USB低优先级中断的处理,CTR_HP:负责USB高优先级中断的处理。 usb_mem.c:处理PMA数据,是stm32内部用于USB/CAN的专用数据缓冲区。PMAToUserBufferCopy:将USB数据传送到主机UserToPMABufferCopy:将主机数据传送到USB。 usb_core.c:处理USB2.0协议。 usb_sil.c:为USB端点提供简化的读写访问函数。
hw_config.c:配置硬件,比如初始化USB时钟、USB中断、低功耗模式处理等。 usb_desc.c:处理描述符。 usb_endp.c:处理正确传输中断回调函数,用于非控制传输 usb_istr.c:处理USB中断 usb_prop.c:处理所有设备相关事件 usb_pwr.c:管理USB控制器的电源
STM32F103 中使用 USB 导入代码。
加入头文件
1 2 3 #include "hw_config.h" #include "usb_lib.h" #include "usb_init.h"
初始化USB
1 2 3 4 5 6 7 8 USB_Port_Set (0 ); delay_ms (700 );USB_Port_Set (1 );Set_USBClock (); USB_Interrupts_Config ();USB_Init ();
STM32F4 中导入USB-OTG开发包 首先去官网下载最新的USB开发包 ,将Libraries下的STM32_USB_Device_Library,STM32_USB_HOST_Library,STM32_USB_OTG_Driver三个文件夹拷贝到项目目录下。其中OTG_Driver是另外两个的基础驱动。在项目中,分别添加这三个目录下的Core文件到项目中,并从中提取以下文件单独放置到Config项目目录下。
Device_Library\Core\inc\usbd_conf_template.h
HOST_Library\Core\inc\usbh_conf_template.h
OTG_Driver\Core\src\usb_bsp_template.h
OTG_Driver\Core\inc\usb_bsp_template.h
Device_Library\Class\cdc\src\usbd_cdc_if_template.c
Device_Library\Class\cdc\inc\usbd_cdc_if_template.h
另外,到Project\USB_Device_Examples下找到VCP目录,复制如下文件到Config下
inc\usb_conf.h
inc\usbd_cdc_vcp.h
inc\usbd_conf.h
inc\usbd_desc.h
src\app.c
src\usb_bsp.c
src\usbd_desc.c
src\usbd_usr.c
更改为合适的名字后,等待下一步修改。
USB_CONF.H 打开开关
1 2 #define USE_USB_OTG_FS #define USE_HOST_MODE
USBD_CONF.H 定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #define USBD_CFG_MAX_NUM 1 #define USBD_ITF_MAX_NUM 4 #define USB_MAX_STR_DESC_SIZ 64 #define USBD_EP0_MAX_PACKET_SIZE 64 #define USBD_SELF_POWERED #define CDC_IN_EP 0x81 #define CDC_OUT_EP 0x01 #define CDC_CMD_EP 0x82 #ifdef USE_USB_OTG_HS #define CDC_DATA_MAX_PACKET_SIZE 64 #define CDC_CMD_PACKET_SZE 8 #define CDC_IN_FRAME_INTERVAL 40 #define APP_RX_DATA_SIZE 2048 #else #define CDC_DATA_MAX_PACKET_SIZE 64 #define CDC_CMD_PACKET_SZE 8 #define CDC_IN_FRAME_INTERVAL 5 #define APP_RX_DATA_SIZE 2048 #endif #define APP_FOPS VCP_fops
USB_DESC.C 修改设备类型
USB_APP.C 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 USBH_HOST USB_Host; USB_OTG_CORE_HANDLE USB_OTG_Core_dev; void OTG_FS_IRQHandler (void ) { if (USB_OTG_IsHostMode (&USB_OTG_Core_dev)) { USBH_OTG_ISR_Handler (&USB_OTG_Core_dev); } else { USBD_OTG_ISR_Handler (&USB_OTG_Core_dev); } } void USBD_Configuration (void ) { USBD_Init (&USB_OTG_Core_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_CDC_cb, &USBD_USR_cb); }
分析 Datasheet 一般在Datasheet里有如下几个部分:
芯片基本信息
电器特性
引脚特性
封装尺寸
参考电路
寄存器和软件
其他
阅读Datasheet的步骤:
快速阅读
应用场景和参考电路
关键参数与指标
涉及到的相关算法,协议,配合器件做延伸阅读
读剩下的部分
对比芯片家族
对比类似产品
一般文档的首页是该芯片的重要信息,有些参数如电源、封装、通道数是属于功能应用信息,而如精度、分辨率等属于性能应用信息。比较而言,功能应用信息首先要关注。
参数:
电器参数,这里只关注重要参数,且要做标记;
极限参数,对于部分需要的极限参数也要看;
管脚参数,防止使用不当。
性能测试图:大概浏览即可。
芯片应用:这部分需要仔细看,搞定时序,逻辑等。
封装信息:在绘制封装时需要看。
最后注意:不要尽信Datasheet,因为可能会有错误。
芯片目录 模拟芯片 数字芯片 主控 电压芯片 AMS-1117:线性降压 SX1308:升压芯片
uC/OS-III 参考 uC/OS-III 代码架构 主要有:配置文件,应用程序,系统代码,库文件,CPU相关代码,BSP板级支持包等。
配置文件:cpu_cfg.h,lib_cfg.h,os_cfg.h,os_cfg_app.h
应用程序:app.c,app.h
库文件:lib_*
系统代码:os_cfg_app.c,os_type.h,os_core.c,os_dgb.c,os_flag.c,os_int.c,os_mem.c,os_msg.c,os_mutex.c,os_pend_multi.c,os_g.c,os_sem.c,os_stat.c,os_tick.c,os_time.c,os_tmr.c,os_var.c,os.h
CPU相关代码:os_cpu.h,os_cpu_a.asm,os_cpu_c.c,cpu_def.h,cpu_c.c,cpu_a.asm,cpu_core.c,cpu_core.h
BSP板级支持包:bsp.h,bsp.c
uC/OS III 代码移植 首先去官方代码下载 ,下载uC/OS III 代码,对于F103可以下载STM32F107的代码,选择Keil版,uC/OS III。
下载完成后,代码目录如下:
Software
EvalBoards
uC-CPU
uC-LIB
uCOS-III
首先在我们的项目文件夹下建立目录:
STM32F103RBT
Project
User
Device
Driver
Ucos
之后:
将代码目录\uC-CPU
以及\uC-CPU\ARM-Cortex-M3\RealView
下的文件(不包括文件夹)全部拷贝到我们的项目目录\STM32F103RBT\Ucos\cpu
下;
将代码目录\uC-LIB
以及\uC-LIB\Ports\ARM-Cortex-M3\RealView
下的文件(不包括文件夹)全部拷贝到我们的项目目录\STM32F103RBT\Ucos\lib
下;
将代码目录\uCOS-III\Source
以及\uCOS-III\Ports\ARM-Cortex-M3\Generic\RealView
下的文件(不包括文件夹)全部拷贝到我们的项目目录\STM32F103RBT\Ucos\core
下;
将代码目录\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III
下的文件(不包括文件夹与stm32f10x_conf.h
)全部拷贝到我们的项目目录\STM32F103RBT\User
下。
将代码目录\EvalBoards\Micrium\uC-Eval-STM32F107\BSP
下的bsp.c
与bsp.h
拷贝到我们的项目目录\STM32F103RBT\User
下。
打开Manage Project Items
按钮,编辑项目目录。这可以按照项目目录创建Groups
,并在对应的Files
中添加相应.c
文件和汇编(.asm
,.a
,.s
)文件。
修改startup_stm32f10x_md.s
:
PendSV_Handler
改为OS_CPU_PendSVHandler
,共3处;
SysTick_Handler
改为OS_CPU_SysTickHandler
,共3处;
修改includes.h
:
#include <stm32f10x_lib.h>
改为#include "stm32f10x.h"
;
修改cpu_cfg.h
:
CPU_CFG_TS_32_EN
后面的选项为DEF_ENABLED
;
CPU_CFG_INT_DIS_MEAS_EN
上面的#if 0
为#if 1
;
修改app_cfg.h
:
APP_CFG_SERIAL_EN
后面的选项为DEF_DISABLED
;
修改lib_cfg.h
:
LIB_MEM_CFG_HEAP_SIZE
后面的大小改为2u * 1024u
修改app.c
:
BSP_IntDisAll();
去掉;
修改AppTaskStart
函数的内容;
删除下面的部分;
1 2 3 4 5 6 7 APP_TRACE_INFO (("Creating Application Tasks...\n\r" ));AppTaskCreate (); APP_TRACE_INFO (("Creating Application Events...\n\r" ));AppObjCreate (); BSP_LED_Off (0 );
修改while中的部分;
1 2 3 4 5 6 7 8 9 10 while (DEF_TRUE) { Led_on (); OSTimeDlyHMSM (0 , 0 , 0 , 100 , OS_OPT_TIME_HMSM_STRICT, &err); Led_off (); OSTimeDlyHMSM (0 , 0 , 0 , 100 , OS_OPT_TIME_HMSM_STRICT, &err); }
修改bsp.h
内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #ifndef BSP_PRESENT #define BSP_PRESENT #ifdef BSP_MODULE #define BSP_EXT #else #define BSP_EXT extern #endif #include <stdarg.h> #include <stdio.h> #include <cpu.h> #include <cpu_core.h> #include <lib_ascii.h> #include <lib_def.h> #include <lib_mem.h> #include <lib_str.h> #include <stm32f10x.h> #include <app_cfg.h> void BSP_Init (void ) ;CPU_INT32U BSP_CPU_ClkFreq (void ) ;void Led_on (void ) ;void Led_off (void ) ;#endif
修改bsp.c
文件为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #define BSP_MODULE #include <bsp.h> CPU_INT32U BSP_CPU_ClkFreq_MHz; #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 DBGMCU_CR_TRACE_IOEN_MASK 0x10 #define DBGMCU_CR_TRACE_MODE_ASYNC 0x00 #define DBGMCU_CR_TRACE_MODE_SYNC_01 0x40 #define DBGMCU_CR_TRACE_MODE_SYNC_02 0x80 #define DBGMCU_CR_TRACE_MODE_SYNC_04 0xC0 #define DBGMCU_CR_TRACE_MODE_MASK 0xC0 #define DEM_CR_TRCENA (1 << 24) #define DWT_CR_CYCCNTENA (1 << 0) void BSP_Init (void ) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE); gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Pin = GPIO_Pin_2; GPIO_Init (GPIOA, &gpio); } void Led_on (void ) { GPIO_ResetBits (GPIOA, GPIO_Pin_2); } void Led_off (void ) { GPIO_SetBits (GPIOA, GPIO_Pin_2); } CPU_INT32U BSP_CPU_ClkFreq (void ) { RCC_ClocksTypeDef rcc_clocks; RCC_GetClocksFreq (&rcc_clocks); return ((CPU_INT32U)rcc_clocks.HCLK_Frequency); } #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
接下来的开发将主要在app.c
和bsp.c
中了。
系统综述 在ucos-iii中,可以创建无数多个任务。另外,在初始化的时候,系统还会窗空闲任务OS_IdleTask()和时基任务OS_TickTask(),以及三个可选任务:软件定时器任务OS_TmrTaks(),中断延迟提交任务OS_IntQTask()和统计任务OS_StatTask()。
任务具有五种状态:
休眠:声明但尚未创建,不受系统管理。
就绪:等待CPU使用权。
运行:正在运行。
等待:等待IO或某事件。
中断服务:进入中断函数。
ucos-iii中对任务定义了9种状态:
OS_TASK_STATE_RDY:就绪状态;
OS_TASK_STATE_DLY:延时状态;
OS_TASK_STATE_DEL:删除状态;
OS_TASK_STATE_SUSPENDED:挂起状态;
OS_TASK_STATE_PEND:无限期等待,直到某事件发生;
OS_TASK_STATE_PEND_TIMEOUT:有限期等待,超时则继续执行;
OS_TASK_STATE_DLY_SUSPENDED:在延时中被挂起;
OS_TASK_STATE_PEND_SUSPENDED:无限等待中被挂起;
OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:优先等待中被挂起
另外,系统还提供软件定时器,多值信号量,互斥信号量,消息队列,事件标志组,任务信号量,消息队列,内存管理(分区)等功能。
中断管理 关闭中断(进入临界区):
1 2 3 void OS_CRITICAL_ENTER (void ) ;void OS_CRITICAL_ENTER_CPU_EXIT (void ) ;
开启中断(退出临界区):
1 2 3 void OS_CRITICAL_EXIT (void ) ;void OS_CRITICAL_EXIT_NO_SCHED (void ) ;
在进入中断服务函数后,应当调用函数将中断嵌套计数器加1,并在退出时减1。
1 2 3 4 void OSIntEnter (void ) ;void OSIntExit (void ) ;
时钟节拍 时钟节拍就是系统以固定的频率产生中断(时基中断),并在中断中处理与时间相关的事件,推动所有任务向前运行。时钟节拍需要依赖于硬件定时器,在STM32裸机程序中经常使用的SysTick
时钟是MCU的内核定时器,通常都使用该定时器产生操作系统的时钟节拍。
用户需要先在os_cfg_app.h
中设定时钟节拍的频率OS_CFG_TICK_RATE_HZ
,该频率越高,操作系统检测事件就越频繁,可以增强任务的实时性,但太频繁也会增加操作系统内核的负担加重,所以用户需要权衡该频率的设置。一般采用1ms的设置,也就是1000Hz。
定时器的初始化是在AppTaskStart()
函数中。在初始化过程中,会开启SysTick
中断,同时在SysTick
中断中会给OS_TickTask()
任务发送信号量。OS_TickTask()
任务收到信号量后就会进入就绪状态,准备运行。
在实际运行中,由软件产生的定时器效果精度较硬件定时器低,误差约在1%以内,一般该精度足够用了。
时间管理 时间管理的相关函数有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 void OSTimeDly ( OS_TICK dly, OS_OPT opt, OS_ERR *p_err ) ;void OSTimeDlyHMSM ( CPU_INT16U hours, CPU_INT16U minutes, CPU_INT16U seconds, CPU_INT32U milli, OS_OPT opt, OS_ERR *p_err ) ;void OSTimeDlyResume ( OS_TCB *p_tcb, OS_ERR *p_err ) ;OS_TICK OSTimeGet ( OS_ERR *p_err ) ;void OSTimeSet ( OS_TICK ticks, OS_ERR *p_err ) ;
软件定时器 软件定时器启动之后是由软件定时器任务OS_TmrTask()
统一管理,在创建软件定时器之前必须先使能软件定时器和配置软件定时器的相关参数。
软件定时器开启位置:os_cfg.h\OS_CFG_TMR_EN
软件定时器配置位置:os_cpu_app.h\OS_CFG_TMR_TASK_PRIO
相应的API有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 void OSTmrCreate ( OS_TMR *p_tmr, CPU_CHAR *p_name, OS_TICK dly, OS_TICK period, OS_OPT opt, OS_TMR_CALLBACK_PTR p_callback, void *p_callback_arg, OS_ERR *p_err ) ;CPU_BOOLEAN OSTmrStart ( OS_TMR *p_tmr, OS_ERR *p_err ) ;CPU_BOOLEAN OSTmrStop ( OS_TMR *p_tmr, OS_OPT opt, void *p_callback_arg, OS_ERR *p_err ) ;CPU_BOOLEAN OSTmrDel ( OS_TMR *p_tmr, OS_ERR *p_err ) ;
软件定时器结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct os_tmr { OS_OBJ_TYPE Type; CPU_CHAR *NamePtr; OS_TMR_CALLBACK_PTR CallbackPtr; void *CallbackPtrArg; OS_TMR *NextPtr; OS_TMR *PrevPtr; OS_TICK Match; OS_TICK Remain; OS_TICK Dly; OS_TICK Period; OS_OPT Opt; OS_STATE State; #if OS_CFG_DBG_EN > 0u OS_TMR *DbgPrevPtr; OS_TMR *DbgNextPtr; #endif };
多值信号量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 void OSSemCreate ( OS_SEM *p_sem, CPU_CHAR *p_name, OS_SEM_CTR cnt, OS_ERR *p_err ) ;OS_SEM_CTR OSSemPost ( OS_SEM *p_sem, OS_OPT opt, OS_ERR *p_err ) ;OS_SEM_CTR OSSemPend ( OS_SEM *p_sem, OS_TICK timeout, OS_OPT opt, CPU_TS *p_ts, OS_ERR *p_err ) ;OS_OBJ_QTY OSSemPendAbort ( OS_SEM *p_sem, OS_OPT opt, OS_ERR *p_err ) ;OS_OBJ_QTY OSSemDel ( OS_SEM *p_sem, OS_OPT opt, OS_ERR *p_err ) ;void OSSemSet ( OS_SEM *p_sem, OS_SEM_CTR cnt, OS_ERR *p_err ) ;
1 2 3 4 5 6 7 8 9 10 11 12 struct os_sem { OS_OBJ_TYPE Type; CPU_CHAR *NamePtr; OS_PEND_LIST PendList; #if OS_CFG_DBG_EN > 0u OS_SEM *DbgPrevPtr; OS_SEM *DbgNextPtr; CPU_CHAR *DbgNamePtr; #endif OS_SEM_CTR Ctr; CPU_TS TS; };
互斥信号量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 void OSMutexCreate ( OS_MUTEX *p_mutex, CPU_CHAR *p_name, OS_ERR *p_err ) ;void OSMutexPost ( OS_MUTEX *p_mutex, OS_OPT opt, OS_ERR *p_err ) ;void OSMutexPend ( OS_MUTEX *p_mutex, OS_TICK timeout, OS_OPT opt, CPU_TS *p_ts, OS_ERR *p_err ) ;OS_OBJ_QTY OSMutexPendAbort ( OS_MUTEX *p_mutex, OS_OPT opt, OS_ERR *p_err ) ;OS_OBJ_QTY OSMutexDel ( OS_MUTEX *p_mutex, OS_OPT opt, OS_ERR *p_err ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct os_mutex { OS_OBJ_TYPE Type; CPU_CHAR *NamePtr; OS_PEND_LIST PendList; #if OS_CFG_DBG_EN > 0u OS_MUTEX *DbgPrevPtr; OS_MUTEX *DbgNextPtr; CPU_CHAR *DbgNamePtr; #endif OS_TCB *OwnerTCBPtr; OS_PRIO OwnerOriginalPrio; OS_NESTING_CTR OwnerNestingCtr; CPU_TS TS; };
消息队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 void OSQCreate ( OS_Q *p_q, CPU_CHAR *p_name, OS_MSG_QTY max_qty, OS_ERR *p_err ) ;void OSQPost ( OS_Q *p_q, void *p_void, OS_MSG_SIZE msg_size, OS_OPT opt, OS_ERR *p_err ) ;void *OSQPend ( OS_Q *p_q, OS_TICK timeout, OS_OPT opt, OS_MSG_SIZE *p_msg_size, CPU_TS *p_ts, OS_ERR *p_err ) ;OS_OBJ_QTY OSQPendAbort ( OS_Q *p_q, OS_OPT opt, OS_ERR *p_err ) ;OS_OBJ_QTY OSQDel ( OS_Q *p_q, OS_OPT opt, OS_ERR *p_err ) ;OS_MSG_QTY OSQFlush ( OS_Q *p_q, OS_ERR *p_err ) ;
1 2 3 4 5 6 7 8 9 10 11 struct os_q { OS_OBJ_TYPE Type; CPU_CHAR *NamePtr; OS_PEND_LIST PendList; #if OS_CFG_DBG_EN > 0u OS_Q *DbgPrevPtr; OS_Q *DbgNextPtr; CPU_CHAR *DbgNamePtr; #endif OS_MSG_Q MsgQ; };
事件标志组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 void OSFlagCreate ( OS_FLAG_GRP *p_grp, CPU_CHAR *p_name, OS_FLAGS flags, OS_ERR *p_err ) ;OS_FLAGS OSFlagPost ( OS_FLAG_GRP *p_grp, OS_FLAGS flags, OS_OPT opt,, OS_ERR *p_err ) ;OS_FLAGS OSFlagPend ( OS_FLAG_GRP *p_grp, OS_FLAGS flags, OS_TICK timeout, OS_OPT opt, CPU_TS *p_ts, OS_ERR *p_err ) ;OS_OBJ_QTY OSFlagPendAbort ( OS_FLAG_GRP *p_grp, OS_OPT opt, OS_ERR *p_err ) ;OS_OBJ_QTY OSFlagDel ( OS_FLAG_GRP *p_grp, OS_OPT opt, OS_ERR *p_err ) ;
1 2 3 4 5 6 7 8 9 10 11 12 struct os_flag_grp { OS_OBJ_TYPE Type; CPU_CHAR *NamePtr; OS_PEND_LIST PendList; #if OS_CFG_DBG_EN > 0u OS_FLAG_GRP *DbgPrevPtr; OS_FLAG_GRP *DbgNextPtr; CPU_CHAR *DbgNamePtr; #endif OS_FLAGS Flags; CPU_TS TS; };
等待多个内核对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 OS_OBJ_QTY OSPendMulti ( OS_PEND_DATA *p_pend_data_tbl, OS_OBJ_QTY tbl_size, OS_TICK timeout, OS_OPT opt, OS_ERR *p_err ) ;
1 2 3 4 5 6 7 8 9 10 struct os_pend_data { OS_PEND_DATA *PrevPtr; OS_PEND_DATA *NextPtr; OS_TCB *TCBPtr; OS_PEND_OBJ *PendObjPtr; OS_PEND_OBJ *RdyObjPtr; void *RdyMsgPtr; OS_MSG_SIZE RdyMsgSize; CPU_TS RdyTS; };
任务信号量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 OS_SEM_CTR OSTaskSemPost ( OS_TCB *p_tcb, OS_OPT opt, OS_ERR *p_err ) ;OS_SEM_CTR OSTaskSemPend ( OS_TICK timeout, OS_OPT opt, CPU_TS *p_ts, CPU_TS *p_ts, OS_ERR *p_err ) ;CPU_BOOLEAN OSTaskSemPendAbort ( OS_TCB *p_tcb, OS_OPT opt, OS_ERR *p_err ) ;
任务消息队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 void OSTaskQPost ( OS_TCB *p_tcb, void *p_void, OS_MSG_SIZE msg_size, OS_OPT opt, OS_ERR *p_err ) ;void *OSTaskQPend ( OS_TICK timeout, OS_OPT opt, OS_MSG_SIZE *p_msg_size, CPU_TS *p_ts, OS_ERR *p_err ) ;CPU_BOOLEAN OSTaskQPendAbort ( OS_TCB *p_tcb, OS_OPT opt, OS_ERR *p_err ) ;
内存管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 void OSMemCreate ( OS_MEM *p_mem, CPU_CHAR *p_name, void *p_addr, OS_MEM_QTY n_blks, OS_MEM_SIZE blk_size, OS_ERR *p_err ) ;void *OSMemGet ( OS_MEM *p_mem, OS_ERR *p_err ) ;void OSMemPut ( OS_MEM *p_mem, void *p_blk, OS_ERR *p_err ) ;
任务管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 void OSTaskCreate ( OS_TCB *p_tcb, CPU_CHAR *p_name, OS_TASK_PTR p_task, void *p_arg, OS_PRIO prio, CPU_STK *p_stk_base, CPU_STK_SIZE stk_limit, CPU_STK_SIZE stk_size, OS_MSG_QTY q_size, OS_TICK time_quanta, ( OSCfg_TickRate_Hz / 10 )。 void *p_ext, OS_OPT opt, OS_ERR *p_err ) ;void OSTaskSuspend ( OS_TCB *p_tcb, OS_ERR *p_err ) ;void OSTaskResume ( OS_TCB *p_tcb, OS_ERR *p_err ) ;void OSTaskChangePrio ( OS_TCB *p_tcb, OS_PRIO prio_new, OS_ERR *p_err ) ;void OSTaskDel ( OS_TCB *p_tcb, OS_ERR *p_err ) ;void OSSchedRoundRobinCfg ( CPU_BOOLEAN en, OS_TICK dflt_time_quanta, OS_ERR *p_err ) ;void OSSchedRoundRobinYield ( OS_ERR *p_err ) ;void OSTaskTimeQuantaSet ( OS_TCB *p_tcb, OS_TICK time_quanta, OS_ERR *p_err ) ;void OSTaskRegSet ( OS_TCB *p_tcb, OS_REG_ID id, OS_REG value, OS_ERR *p_err ) ;OS_REG OSTaskRegGet ( OS_TCB *p_tcb, OS_REG_ID id, OS_ERR *p_err ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 struct os_tcb { CPU_STK *StkPtr; void *ExtPtr; CPU_STK *StkLimitPtr; OS_TCB *NextPtr; OS_TCB *PrevPtr; OS_TCB *TickNextPtr; OS_TCB *TickPrevPtr; OS_TICK_SPOKE *TickSpokePtr; CPU_CHAR *NamePtr; CPU_STK *StkBasePtr; #if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u) OS_TLS TLS_Tbl[OS_CFG_TLS_TBL_SIZE]; #endif OS_TASK_PTR TaskEntryAddr; void *TaskEntryArg; OS_PEND_DATA *PendDataTblPtr; OS_STATE PendOn; OS_STATUS PendStatus; OS_STATE TaskState; OS_PRIO Prio; CPU_STK_SIZE StkSize; OS_OPT Opt; OS_OBJ_QTY PendDataTblEntries; CPU_TS TS; OS_SEM_CTR SemCtr; OS_TICK TickCtrPrev; OS_TICK TickCtrMatch; OS_TICK TickRemain; OS_TICK TimeQuanta; OS_TICK TimeQuantaCtr; #if OS_MSG_EN > 0u void *MsgPtr; OS_MSG_SIZE MsgSize; #endif #if OS_CFG_TASK_Q_EN > 0u OS_MSG_Q MsgQ; #if OS_CFG_TASK_PROFILE_EN > 0u CPU_TS MsgQPendTime; CPU_TS MsgQPendTimeMax; #endif #endif #if OS_CFG_TASK_REG_TBL_SIZE > 0u OS_REG RegTbl[OS_CFG_TASK_REG_TBL_SIZE]; #endif #if OS_CFG_FLAG_EN > 0u OS_FLAGS FlagsPend; OS_FLAGS FlagsRdy; OS_OPT FlagsOpt; #endif #if OS_CFG_TASK_SUSPEND_EN > 0u OS_NESTING_CTR SuspendCtr; #endif #if OS_CFG_TASK_PROFILE_EN > 0u OS_CPU_USAGE CPUUsage; OS_CPU_USAGE CPUUsageMax; OS_CTX_SW_CTR CtxSwCtr; CPU_TS CyclesDelta; CPU_TS CyclesStart; OS_CYCLES CyclesTotal; OS_CYCLES CyclesTotalPrev; CPU_TS SemPendTime; CPU_TS SemPendTimeMax; #endif #if OS_CFG_STAT_TASK_STK_CHK_EN > 0u CPU_STK_SIZE StkUsed; CPU_STK_SIZE StkFree; #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN CPU_TS IntDisTimeMax; #if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u CPU_TS SchedLockTimeMax; #endif #if OS_CFG_DBG_EN > 0u OS_TCB *DbgPrevPtr; OS_TCB *DbgNextPtr; CPU_CHAR *DbgNamePtr; #endif };
中断管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 void OSIntEnter (void ) ;void OSIntExit (void ) ;CPU_TS_TMR CPU_IntDisMeasMaxGet (void ) ;CPU_TS_TMR CPU_IntDisMeasMaxCurReset (void ) ;CPU_TS_TMR CPU_IntDisMeasMaxCurGet (void )
统计信息 统计信息的开关在:os_cfg.h\OS_CFG_STAT_TASK_EN
。
CPU 使用率和其最大记录分别保存于全局变量OSStatTaskCPUUsage
和OSStatTaskCPUUsageMax
,一个任务的CPU 使用率和其最大记录分别保存于其任务控制块的CPUUsage
和CPUUsageMax
成员,一个任务的任务堆栈的空闲大小和已用大小分别保存于其任务控制块的StkFree
和StkUsed
成员。需要注意,OSStatTaskCPUUsage
、OSStatTaskCPUUsageMax
、CPUUsage
和CPUUsageMax
均被放大了10000 倍,所以这几个值缩小10000 倍后才是真实值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 CPU_INT32U BSP_CPU_ClkFreq (void ) ;CPU_INT16U OSVersion ( OS_ERR *p_err ) ;
除了前面讲述的统计信息外,uC/OS 系统还为我们提供了很多统计信息。前一章讲述的最大关中断时间就是个很重要参数。此外,全局变量OSTaskCtxSwCtr 记录了任务切换总次数,OSTaskQty 记录了被创建任务的数目, OSSchedLockTimeMax ( 需使能OS_CFG_SCHED_LOCK_TIME_MEAS_EN,位于“os_cfg.h”)记录了调度器被锁的最大时间,OSIntQNbrEntriesMax 记录了中断队列成员被使用的最大数目,OSFlagQty 记录了事件标志组对象的数目,OSMemQty 记录了内存管理(分区)对象的数目,OSMutexQty 记录了互斥信号量对象的数目,OSQQty 记录了消息队列对象的数目,OSSemQty 记录了多值信号量对象的数目,等等。
uC/OS-II 与 uC/OS-III 区别
功能
uC/OS-II
uC/OS-III
最大任务数
256
无限制
每个优先级的任务数
1
无限制
时间片轮转
否
是
互斥信号量
不可嵌套
可嵌套
消息邮箱
是
否
不通过信号量标记任务
否
是
不通过消息队列发送消息
否
是
任务的停止与恢复
不可嵌套
可嵌套
代码段需求
6k~26k
6k~20k
运行时配置
否
是
嵌入的测量功能
有限制
大量的
时间戳
否
是
汇编可视化
否
是
任务级的时基定时器处理
否
是
uC/OS-II 参考 代码 代码分为与处理器相关和无关的代码,以及应用相关的代码。
处理器无关的代码有:ucos_ii.h,ucos_ii.c,os_tmr.c,os_time.c,os_task.c,os_sem.c,os_q.c,os_mutex.c,os_mem.c,os_mbox.c,os_flag.c,os_core.c
处理器相关的代码有:os_cpu.h,os_cou_a.asm,os_cpu_c.c
应用相关的代码有:os_cfg.h,includes.h
任务管理 任务是由程序,数据和PCB构成,任务之间由链表连接。
任务管理的相关函数:
1 2 3 4 5 6 7 8 9 OSTaskCreate () OSTaskCreateExt () OSTaskStkChk () OSTaskDel () OSTaskDelReq () OSTaskSuuspend () OSTaskResume () OSTaskChangePrio () OSTaskQuery ()
创建任务要求:
创建任务不能在中断中创建;
且优先级范围是0~63,每个优先级与每个任务一一对应;
系统在运行期间可以创建任务。
创建任务:
1 2 3 4 5 6 7 8 9 10 INT8U OSTaskCreate (void (*task)(void *p_arg), void *pdata, OS_STK *ptos, INT8U prio) ;
其他任务函数
1 2 3 4 5 6 7 8 9 10 11 12 INT8U OSTaskDel (INT8U prio) INT8U OSTaskSuspend (INT8U prio) INT8U OSTaskResume (INT8U prio) INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio) INT8U OSTaskChangePrio (INT8U prio, OS_TCB *pdata)
结构体OS_TCB主要包含:
栈顶指针
控制块扩展指针
栈底指针
堆栈长度
创建任务的选项
保留
后一个TCB指针
前一个TCB指针
任务等待的时限
任务状态
优先级
用于快速访问就绪表的数据
例如创建函数的使用:
1 2 3 4 5 6 7 8 9 10 void main (void ) { OSInit (); OSTaskCreate (Task, args); OSTaskCreate (Task, args); OSStart (); }
任务代码结构:
1 2 3 4 5 6 7 8 9 10 11 void Task (void *pdata) { while (1 ){ OS_ENTER_CRITICAL (); OS_EXIT_CRITICAL (); } }
另外还有空闲任务和统计任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void OSTaskIddle (void *pdata) { pdata = pdata; for (;;){ OS_ENTER_CRITICAL (); OSIdleCtr++; OS_EXIT_CRITICAL (); } } OSTaskStart ()
任务调度 调度器负责在任务就绪队列中选取任务,并将切换出的任务放入等待队列。调度时机有:
操作系统运行开始的时候
任务用完一个时间片
产生了一个中断
任务自身转为等待
任务自身被删除
1 2 3 4 5 6 void OSSchedLock (void ) ;void OSSchedUnlock (void ) ;void OSSched (void ) ;
时钟 ucos必须需要一个定时器用来做系统时钟。开启时钟必须在OSStart()之后,也就是用户任务中开启。
时钟管理的相关函数:
1 2 3 4 5 6 OSTickISR () OSTimeDly () OSTimeDlyHMSM () OSTimeDlyResume () OSTimeGet () OSTimeSet ()
信号量 1 2 3 4 5 6 OSSemCreate () OSSemPend () OSSemPost () OSSemDel () OSSemAccept () OSSemQuery ()
消息邮箱 1 2 3 4 5 6 7 OSMboxCreate () OSMboxPend () OSMboxPost () OSMboxPostOpt () OSMboxDel () OSMboxAccept () OSMboxQuery ()
消息队列 1 2 3 4 5 6 7 8 OSQCreate () OSQPend () OSQPostFront () OSQPostOpt () OSQFlush () OSQDel () OSQQuery () OSQAccept ()
内存管理 采用固定分区法。
1 2 3 4 5 6 OSMemCreate () OSMemGet () OSMemPut () OSMemQuery () OSMemNameGet () OSMemNameSet ()