STM32

Keil 标准库

创建STM32F103工程

首先创建一个项目目录:

  • STM32F103RBT
    • Project
    • User
    • Device
    • Driver

打开Keil,选择上方的Project->New uVersion Project,在Project目录下创建工程文件。在打开的面板左侧选择版型,这里选择STM32F1 Series->STM32F103->STM32F103C8

下一步会弹出Manage Run-Time Environment,这里是选择外设的地方。
这里选择需要的库:

  • CMSIS
    • CORE
  • 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,可以是JtagSW,可根据下载器选择。完成后确定返回。

这里开始创建一个main.c和一个main.h文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// main.c

#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
// main.h
#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,配置如下选项:

  1. 找到PLL Parameters下的#if defined(STM32F40_41xxx)
  2. 配置PLL_M25改为8
  3. 配置PLL_N保持为336
  4. 配置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
/*
请求类型:
D7:数据传输方向,0 Host -> Device, 1 Device -> Host
D65:请求类型,0 标准,1 类,2 厂商,3 保留
D43210:请求的接收者,0 设备,1 接口,2 端点,3 其他,其余保留
*/
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;

/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));

#define USB_DT_ENDPOINT_SIZE 7

/* Audio extension */
#define USB_DT_ENDPOINT_AUDIO_SIZE 9

字符串描述符:

1
2
3
4
5
6
struct usb_string_descriptor {
__u8 bLength;
__u8 bDescriptorType;

__le16 wData[1]; /* UTF-16LE encoded */
} __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
/* All Descriptors (配置描述符, 接口描述符, 端点描述符, 类描述符 */
const uint8_t Composite_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =
{
配置描述符 //Configuration Descriptor 只能有1个

/*功能1——VCP虚拟串口接口*/
IAD描述符 //复合设备才有 在单接口的设备这个可以不要
接口1描述符 //Interface Descriptor
类描述符 //Class Desdriptor
端点描述符 //Endpoint Descriptor

接口2描述符 //Interface Descriptor
类描述符 //Class Desdriptor
端点描述符 //Endpoint Descriptor
端点描述符 //Endpoint Descriptor如果此接口有多个端点,可接着添加端点描述符

/*如果有多个接口 下面还可以继续添加以下描述符*/
/*功能2——HID键盘*/
IAD描述符 //复合设备才有 在单接口的设备这个可以不要
接口描述符 //Interface Descriptor
类描述符 //Class Desdriptor
端点描述符 //Endpoint Descriptor

/*如果有多个接口 下面还可以继续添加以下描述符*/
/*接口3 */
...
}

const uint8_t Composite_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =
{
0x09, /* bLength: Configuration Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
CUSTOMHID_SIZ_CONFIG_DESC,
/* wTotalLength: Bytes returned */
0x00,
0x03, /* bNumInterfaces: 3 interface 共3个接口 CDC 2个 HID一个*/
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing
the configuration*/
0xC0, /* bmAttributes: Self powered */
0x32, /* MaxPower 100 mA: this current is used for detecting Vbus */

/*************************************功能1 HID键盘**************************************/
/*IAD描述符*/
0x08, //bLength:IAD描述符大小
0x0B, //bDescriptorType:IAD描述符类型
0x00, //bFirstInterface:功能1 HID键盘的第一个接口描述符是在总的配置描述符中的第几个从0开始数
0x01, //bInferfaceCount:功能1 HID键盘有1个接口描述符
0x03, //bFunctionClass:同单HID功能时,设备符中的bDeviceClass
0x00, //bFunctionSubClass:同单HID功能时,设备符中的bDeviceSubClass
0x01, //bFunctionProtocol:同单HID功能时,设备符中的bDeviceProtocol
0x00, //iFunction:字符串描述中关于此设备的索引(个人理解是一个字符串描述符中有比如0~5是功能1的字符串,
//6~10是功能2的字符串,如果是功能2的话,此值为6)


/************** Descriptor of Custom HID interface ****************/
/* 09 */
0x09, /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType: Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */ //<接口 0>
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints */
0x03, /* bInterfaceClass: HID */
0x01, /* bInterfaceSubClass : 1=BOOT, 0=no boot */
0x01, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */
0, /* iInterface: Index of string descriptor */
/******************** Descriptor of Custom HID HID ********************/
/* 18 */
0x09, /* bLength: HID Descriptor size */
HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID */
0x10, /* bcdHID: HID Class Spec release number */
0x01,
0x00, /* bCountryCode: Hardware target country */
0x01, /* bNumDescriptors: Number of HID class descriptors to follow */
0x22, /* bDescriptorType */
CUSTOMHID_SIZ_REPORT_DESC,
//KEYBOARD_SIZ_REPORT_DESC,/* wItemLength: Total length of Report descriptor */
0x00,
/******************** Descriptor of Custom HID endpoints ******************/
/* 27 */
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x84, /* bEndpointAddress: Endpoint Address (IN) */
0x03, /* bmAttributes: Interrupt endpoint */
0x08, /* wMaxPacketSize: 8 Bytes max */
0x00,
0x20, /* bInterval: Polling Interval (32 ms) */
/* 34 */
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x04, /* bEndpointAddress:Endpoint Address (OUT) */
0x03, /* bmAttributes: Interrupt endpoint */
0x01, /* wMaxPacketSize: 1 Bytes max */
0x00,
0x20, /* bInterval: Polling Interval (20 ms) */
/* 41 */
/********************************功能2 VCP虚拟串口*****************************/
/*IAD描述符*/
/* Interface Association Descriptor(IAD Descriptor) */
0x08, /* bLength */
0x0B, /* bDescriptorType*/
0x01, /* bFirstInterface*/
0x02, /* bInterfaceCount*/
0x02, /* bFunctionClass --CDC*/
0x02, /* bFunctionSubClass*/
0x01, /* bFunctionProtocoll*/
0x00, /* iFunction */

/**VCP虚拟串口**/
/*Interface Descriptor接口描述符*/
0x09, /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
/* Interface descriptor type */
0x01, /* bInterfaceNumber: Number of Interface */ //<接口 1>
0x00, /* bAlternateSetting: Alternate setting */
0x01, /* bNumEndpoints: One endpoints used 该接口非0端点数*/
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x01, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
/*Header Functional Descriptor类描述符*/
0x05, /* bLength: Endpoint Descriptor size */
0x24, /* bDescriptorType: CS_INTERFACE */
0x00, /* bDescriptorSubtype: Header Func Desc */
0x10, /* bcdCDC: spec release number */
0x01,
/*Call Management Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */
0x01, /* bDataInterface: 1 */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
0x02, /* bmCapabilities */
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
0x00, /* bMasterInterface: Communication class interface */
0x01, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor端点描述符*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x82, /* bEndpointAddress: (IN2) */
0x03, /* bmAttributes: Interrupt */
VIRTUAL_COM_PORT_INT_SIZE, /* wMaxPacketSize: */
0x00,
0xFF, /* bInterval: */

/*Data class interface descriptor类描述符*/
0x09, /* bLength: Endpoint Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x02, /* bInterfaceNumber: Number of Interface *///<接口 2>
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0x0A, /* bInterfaceClass: CDC */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint 3 Descriptor端点描述符*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x03, /* bEndpointAddress: (OUT3) */
0x02, /* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE, /* wMaxPacketSize: */
0x00,
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint 1 Descriptor 端点描述符*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x81, /* bEndpointAddress: (IN1) */
0x02, /* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE, /* wMaxPacketSize: */
0x00,
0x00 /* bInterval */
}; /* CustomHID_ConfigDescriptor */

另外,对于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:0000 01 01 这是个全局条目,用途页选择为普通桌面页
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
//0x09:0000 10 01 这是个全局条目,用途选择为键盘
0x09, 0x06, // USAGE (Keyboard)
//0xa1:1010 00 01 这是个主条目,选择为应用集合,
0xa1, 0x01, // COLLECTION (Application)
//0x05:0000 01 11 这是个全局条目,用途页选择为键盘/按键
0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)

//0x19:0001 10 01 这是个局部条目,用途的最小值为0xe0,对应键盘上的左ctrl键
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
//0x29:0010 10 01 这是个局部条目,用途的最大值为0xe7,对应键盘上的有GUI(WIN)键
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
//0x15:0001 01 01 这是个全局条目,说明数据的逻辑值最小值为0
0x15, 0x00, // LOGICAL_MINIMUM (0)
//0x25:0010 01 01 这是个全局条目,说明数据的逻辑值最大值为1
0x25, 0x01, // LOGICAL_MAXIMUM (1)

//0x95:1001 01 01 这是个全局条目,数据域的数量为8个
0x95, 0x08, // REPORT_COUNT (8)
//0x75:0111 01 01 这是个全局条目,每个数据域的长度为1位
0x75, 0x01, // REPORT_SIZE (1)
//0x81:1000 00 01 这是个主条目,有8*1bit数据域作为输入,属性为:Data,Var,Abs
0x81, 0x02, // INPUT (Data,Var,Abs)

//0x95:1001 01 01 这是个全局条目,数据域的数量为1个
0x95, 0x01, // REPORT_COUNT (1)
//0x75:0111 01 01 这是个全局条目,每个数据域的长度为8位
0x75, 0x08, // REPORT_SIZE (8)
//0x81:1000 00 01 这是个主条目,有1*8bit数据域作为输入,属性为:Cnst,Var,Abs
0x81, 0x03, // INPUT (Cnst,Var,Abs)

//0x95:1001 01 01 这是个全局条目,数据域的数量为6个
0x95, 0x06, // REPORT_COUNT (6)
//0x75:0111 01 01 这是个全局条目,每个数据域的长度为8位
0x75, 0x08, // REPORT_SIZE (8)
//0x25:0010 01 01 这是个全局条目,逻辑最大值为255
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
//0x19:0001 10 01 这是个局部条目,用途的最小值为0
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
//0x29:0010 10 01 这是个局部条目,用途的最大值为0x65
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
//0x81:1000 00 01 这是个主条目,有6*8bit的数据域作为输入,属相为属性为:Data,Var,Abs
0x81, 0x00, // INPUT (Data,Ary,Abs)

//0x25:0010 01 01 这是个全局条目,逻辑的最大值为1
0x25, 0x01, // LOGICAL_MAXIMUM (1)
//0x95:1001 01 01 这是个全局条目,数据域的数量为2
0x95, 0x02, // REPORT_COUNT (2)
//0x75:0111 01 01 这是个全局条目,每个数据域的长度为1位
0x75, 0x01, // REPORT_SIZE (1)
//0x05:0000 01 01 这是个全局条目,用途页选择为LED页
0x05, 0x08, // USAGE_PAGE (LEDs)
//0x19:0001 10 01 这是个局部条目,用途的最小值为0x01,对应键盘上的Num Lock
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
//0x29:0010 10 01 这是个局部条目,用途的最大值为0x02,对应键盘上的Caps Lock
0x29, 0x02, // USAGE_MAXIMUM (Caps Lock)
//0x91:1001 00 01 这是个主条目,有2*1bit的数据域作为输出,属性为:Data,Var,Abs
0x91, 0x02, // OUTPUT (Data,Var,Abs)

//0x95:1001 01 01 这是个全局条目,数据域的数量为1个
0x95, 0x01, // REPORT_COUNT (1)
//0x75:0111 01 01 这是个全局条目,每个数据域的长度为6bit,正好与前面的2bit组成1字节
0x75, 0x06, // REPORT_SIZE (6)
//0x91:1001 00 01 这是个主条目,有1*6bit数据域最为输出,属性为:Cnst,Var,Abs
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)

0xc0 // END_COLLECTION

}; /* CustomHID_ReportDescriptor */

常见 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
USB_Port_Set(0);
delay_ms(700);
USB_Port_Set(1);
// 初始化 USB
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

// VCP

#define CDC_IN_EP 0x81 /* EP1 for data IN */
#define CDC_OUT_EP 0x01 /* EP1 for data OUT */
#define CDC_CMD_EP 0x82 /* EP2 for CDC commands */

#ifdef USE_USB_OTG_HS
#define CDC_DATA_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */
#define CDC_CMD_PACKET_SZE 8 /* Control Endpoint Packet size */

#define CDC_IN_FRAME_INTERVAL 40 /* Number of micro-frames between IN transfers */
#define APP_RX_DATA_SIZE 2048 /* Total size of IN buffer:
APP_RX_DATA_SIZE*8/MAX_BAUDARATE*1000 should be > CDC_IN_FRAME_INTERVAL*8 */
#else
#define CDC_DATA_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */
#define CDC_CMD_PACKET_SZE 8 /* Control Endpoint Packet size */

#define CDC_IN_FRAME_INTERVAL 5 /* Number of frames between IN transfers */
#define APP_RX_DATA_SIZE 2048 /* Total size of IN buffer:
APP_RX_DATA_SIZE*8/MAX_BAUDARATE*1000 should be > CDC_IN_FRAME_INTERVAL */
#endif /* USE_USB_OTG_HS */

#define APP_FOPS VCP_fops

USB_DESC.C

修改设备类型

1
2
3
// USBD_DeviceDesc
0x02, /* bDeviceClass */
0x00, /* bDeviceSubClass */

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)) //È·¶¨ÊDz»ÊÇUSBÖ÷»úģʽ?
{
USBH_OTG_ISR_Handler(&USB_OTG_Core_dev);//USBÖ÷»úÖжÏ
}
else
{
USBD_OTG_ISR_Handler(&USB_OTG_Core_dev);//USB´Ô»úÖжÏ
}
}

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
      • Micrium
        • uC-Eval-STM32F107
          • BSP
          • uCOS-III
    • uC-CPU
      • ARM-Cortex-M3
        • GNU
        • IAR
        • RealView
    • uC-LIB
      • Port
        • ARM-Cortex-M3
          • GNU
          • IAR
          • RealView
    • uCOS-III
      • Source
      • Ports
        • ARM-Cortex-M3
          • GNU
          • IAR
          • RealView

首先在我们的项目文件夹下建立目录:

  • STM32F103RBT
    • Project
    • User
    • Device
    • Driver
    • Ucos
      • lib
      • core
      • cpu

之后:

  • 将代码目录\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.cbsp.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(); /* Create Application Tasks */

APP_TRACE_INFO(("Creating Application Events...\n\r"));
AppObjCreate(); /* Create Application Objects */

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; /* 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

接下来的开发将主要在app.cbsp.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
// 中断嵌套计数 + 1
void OSIntEnter(void);
// 中断嵌套计数 - 1
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
/*
延时执行当前任务,延时结束后再回来执行当前任务。

选项:
OS_OPT_TIME_DLY Dly 为相对时间,就是从现在起延时多长时间, 到时钟节拍总计数OSTickCtr = OSTickCtr 当前+ dly 时延时结束。
OS_OPT_TIME_TIMEOUT 跟OS_OPT_TIME_DLY 情况一样。
OS_OPT_TIME_MATCH Dly 为绝对时间,就是从系统开始运行(调用OSStart())时到节拍总计数OSTickCtr = dly 时延时结束。
OS_OPT_TIME_PERIODIC 周期性延时,跟OS_OPT_TIME_DLY差不多。如果是长时间延时,该选项更精准一些。

错误:
OS_ERR_NONE 没错误。
OS_ERR_OPT_INVALID 选项不可用。
OS_ERR_SCHED_LOCKED 调度器被锁
OS_ERR_TIME_DLY_ISR 在中断中使用该函数。
OS_ERR_TIME_ZERO_DLY Dly 为0。
*/
void OSTimeDly(
OS_TICK dly, // 延时的节拍数
OS_OPT opt,
OS_ERR *p_err
);

/*
延时执行当前任务,延时结束后再回来执行当前任务。
开关:os_cfg.h\OS_CFG_TIME_DLY_HMSM_EN

选项:
OS_OPT_TIME_DLY Dly 为相对时间,就是从现在起延时多长时间, 到时钟节拍总计数OSTickCtr = OSTickCtr 当前+ dly 时延时结束。
OS_OPT_TIME_TIMEOUT 跟OS_OPT_TIME_DLY 情况一样。
OS_OPT_TIME_MATCH Dly 为绝对时间,就是从系统开始运行(调用OSStart())时到节拍总计数OSTickCtr = dly 时延时结束。
OS_OPT_TIME_PERIODIC 周期性延时,跟OS_OPT_TIME_DLY差不多。如果是长时间延时,该选项更精准一些。
OS_OPT_TIME_HMSM_STRICT 延时时间取值比较严格。
hours (0...99)
minutes (0...59)
seconds (0...59)
milliseconds (0...999)
OS_OPT_TIME_HMSM_NON_STRICT 延时时间取值比较宽松。
hours (0...999)
minutes (0...9999)
seconds (0...65535)
milliseconds (0...4294967295)

错误:
OS_ERR_NONE 没错误。
OS_ERR_OPT_INVALID 选项不可用。
OS_ERR_SCHED_LOCKED 调度器被锁
OS_ERR_TIME_DLY_ISR 在中断中使用该函数。
OS_ERR_TIME_INVALID_HOURS 小时数不可用。
OS_ERR_TIME_INVALID_MINUTES 分钟数不可用。
OS_ERR_TIME_INVALID_SECONDS 秒数不可用。
OS_ERR_TIME_INVALID_MILLISECONDS 毫秒数不可用。
OS_ERR_TIME_ZERO_DLY 延时时间为0。
*/

void OSTimeDlyHMSM (
CPU_INT16U hours, // 小时数
CPU_INT16U minutes, // 分钟数
CPU_INT16U seconds, // 秒数
CPU_INT32U milli, // 毫秒数
OS_OPT opt,
OS_ERR *p_err
);

/*
结束其他任务的延时。

错误:
OS_ERR_NONE 没错误。
OS_ERR_STATE_INVALID 任务状态非法。
OS_ERR_TIME_DLY_RESUME_ISR 在中断中结束延时。
OS_ERR_ TASK_NOT_DLY 任务不在延时。
OS_ERR_TASK_SUSPENDED 任务被挂起。

开关:os_cfg.h\OS_CFG_TIME_DLY_RESUME_EN
该函数的p_tcb 参数不可以是当前任务。
不可以用该函数结束等待事件。
*/

void OSTimeDlyResume (
OS_TCB *p_tcb, // 任务的任务控制块指针
OS_ERR *p_err
);

/*
获取当前的时钟节拍计数值。

错误:
OS_ERR_NONE 没错误。

返回:当前的时钟节拍计数值。
*/
OS_TICK OSTimeGet (
OS_ERR *p_err
);

/*
设置当前的时钟节拍计数值。

错误:
OS_ERR_NONE 没错误。

该函数谨慎使用。
*/

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
/*
创建一个软件定时器。

选项:
OS_OPT_TMR_ONE_SHOT 周期性定时。
OS_OPT_TMR_PERIODIC 一次性定时。

错误:
OS_ERR_NONE 没错误
OS_ERR_ILLEGAL_CREATE_RUN_TIME 非法创建内核对象
OS_ERR_OBJ_CREATED 该定时器已被创建过
OS_ERR_OBJ_PTR_NULL 定时器对象为空
OS_ERR_OBJ_TYPE 定时器对象无效
OS_ERR_OPT_INVALID 选项不可用。
OS_ERR_TMR_INVALID_DLY 定时初始实参无效
OS_ERR_TMR_INVALID_PERIOD 周期重载实参无效
OS_ERR_TMR_ISR 在中断函数中定时

一次性定时时dly 不能为0。
周期性定时时period 不能为0。
*/
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
);

/*
启动一个软件定时器。

错误:
OS_ERR_NONE 没错误
OS_ERR_ILLEGAL_CREATE_RUN_TIME 非法创建内核对象
OS_ERR_OBJ_CREATED 该定时器已被创建过
OS_ERR_OBJ_PTR_NULL 定时器对象为空
OS_ERR_OBJ_TYPE 定时器对象无效
OS_ERR_OPT_INVALID 选项不可用。
OS_ERR_TMR_INVALID_DLY 定时初始实参无效
OS_ERR_TMR_INVALID_PERIOD 周期重载实参无效
OS_ERR_TMR_ISR 在中断函数中定时

返回:
DEF_TRUE,执行成功。
DEF_FALSE,执行失败。

一次性定时时dly 不能为0。
周期性定时时period 不能为0。
*/

CPU_BOOLEAN OSTmrStart (
OS_TMR *p_tmr, // 定时器控制块指针。
OS_ERR *p_err
);


/*
停止一个软件定时器。

选项:
OS_OPT_TMR_NONE 只需停止定时器,不需执行指定事件。
OS_OPT_TMR_CALLBACK 停止定时器,并执行回调函数。
OS_OPT_TMR_CALLBACK_ARG 停止定时器,并执行回调函数,且将p_callback_arg 作为新实参。

错误:
OS_ERR_NONE 没错误
OS_ERR_OBJ_TYPE p_tmr 不是一个定时器指针
OS_ERR_OPT_INVALID opt 非法
OS_ERR_TMR_INACTIVE 定时器为被创建
OS_ERR_TMR_INVALID p_tmr 为空
OS_ERR_TMR_INVALID_STATE 定时器状态非法
OS_ERR_TMR_ISR 在中断中被调用
OS_ERR_TMR_NO_CALLBACK 定时器不存在回调函数
OS_ERR_TMR_STOPPED 定时器已被停止

返回:
DEF_TRUE , 停止成功( 包括定时器已被停止, 即错误类型为“OS_ERR_TMR_STOPPED”)。
DEF_FALSE,停止失败。
*/

CPU_BOOLEAN OSTmrStop (
OS_TMR *p_tmr,
OS_OPT opt,
void *p_callback_arg,
OS_ERR *p_err
);

/*
删除一个软件定时器。

错误:
OS_ERR_NONE 没错误
OS_ERR_OBJ_TYPE p_tmr 不是一个定时器类型
OS_ERR_TMR_INVALID p_tmr 为空
OS_ERR_TMR_ISR 在中断中被调用
OS_ERR_TMR_INACTIVE 定时器未被创建过
OS_ERR_TMR_INVALID_STATE 定时器处于非法状态

返回:
DEF_TRUE,删除成功;
DEF_FALSE,删除失败,或者有错误。
*/
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
/*
创建一个多值信号量。

错误:
OS_ERR_NONE 没错误
OS_ERR_CREATE_ISR 在中断中调用该函数
OS_ERR_ILLEGAL_CREATE_RUN_TIME 在调用OSSafetyCriticalStart()函数后创建内核对象
OS_ERR_NAME p_name 为空指针
OS_ERR_OBJ_CREATED 信号量已经被创建过
OS_ERR_OBJ_PTR_NULL p_sem 是个空指针
OS_ERR_OBJ_TYPE p_sem 已被初始化到另一种对象类型
*/

void OSSemCreate (
OS_SEM *p_sem, // 多值信号量指针。
CPU_CHAR *p_name, // 多值信号量名称。
OS_SEM_CTR cnt, // 资源数目。
OS_ERR *p_err
);

/*
发布多值信号量。

选项:
OS_OPT_POST_1 发布给等待该信号量中最高优先级的任务。
OS_OPT_POST_ALL 发布给等待该信号量的所有任务。
OS_OPT_POST_1 |OS_OPT_POST_NO_SCHED发布给等待该信号量中最高优先级的任务,但不进行任务调度。
OS_OPT_POST_ALL |OS_OPT_POST_NO_SCHED发布给等待该信号量的所有任务,但不进行任务调度。

错误:
OS_ERR_NONE 没错误
OS_ERR_OBJ_PTR_NULL p_sem 为空
OS_ERR_OBJ_TYPE p_sem 不是多值信号量类型对象
OS_ERR_SEM_OVF 该发布将导致了信号量的计数值溢出

返回:
0,有错误。
其他值,信号量的计数值。
*/
OS_SEM_CTR OSSemPost (
OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err
);

/*
等待一个多值信号量。

选项:
OS_OPT_PEND_BLOCKING 如果不能立即获得信号量,就堵塞当前任务,继续等待信号量。
OS_OPT_PEND_NON_BLOCKING 如果不能立即获得信号量,不堵塞当前任务,不继续等待信号量。

错误:
OS_ERR_NONE 没错误,获得信号量。
OS_ERR_OBJ_DEL p_sem 被删除。
OS_ERR_OBJ_PTR_NULL p_sem 为空。
OS_ERR_OBJ_TYPE p_sem 不是信号量类型对象。
OS_ERR_OPT_INVALID opt 非法。
OS_ERR_PEND_ABORT 等待被另一个任务中止。
OS_ERR_PEND_ISR 在中断中被调用。
OS_ERR_PEND_WOULD_BLOCK 缺乏堵塞。
OS_ERR_SCHED_LOCKED 调度器被锁。
OS_ERR_STATUS_INVALID 等待状态非法。
OS_ERR_TIMEOUT 等待超时。

返回:
0,信号量的当前计数值为0,或有错误。
其他值,信号量的当前计数值。
*/

OS_SEM_CTR OSSemPend (
OS_SEM *p_sem,
OS_TICK timeout, // 等待超时时间(单位:时钟节拍),0代表无期限等待。opt 为OS_OPT_PEND_BLOCKING 时该参数才起作用。
OS_OPT opt,
CPU_TS *p_ts, // 时间戳,用于存储信号量最后一次被发布的时间戳,或者等待被中止的时间戳,或者信号量被删除时的时间戳,具体返回哪个时间戳,要根据返回的p_err 判断。该参数可以为NULL,表示用户不需要获得时间戳。
OS_ERR *p_err
);

/*
中止对一个多值信号量的等待。

选项:
OS_OPT_PEND_ABORT_1 只中止该信号量等待列表中的最高优先级任务。
OS_OPT_PEND_ABORT_ALL 中止该信号量等待列表中的所有优先级任务。
OS_OPT_PEND_ABORT_1|OS_OPT_POST_NO_SCHED 只中止该信号量等待列表中的最高优先级任务,但不进行任务调度。
OS_OPT_PEND_ABORT_ALL|OS_OPT_POST_NO_SCHED 中止该信号量等待列表中的所有优先级任务,但不进行任务调度。

错误:
OS_ERR_NONE 没错误。
OS_ERR_OBJ_PTR_NULL p_sem 为空。
OS_ERR_OBJ_TYPE p_sem 不是多值信号量类型。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_PEND_ABORT_ISR 该函数在中断中被调用。
OS_ERR_PEND_ABORT_NONE 没有任务在等待该信号量。

返回:
0,没有任务在等待该信号量,或者有错误产生。
>0,被中止的任务数。
*/
OS_OBJ_QTY OSSemPendAbort (
OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err
);

/*
删除一个多值信号量。

选项:
OS_OPT_DEL_NO_PEND 如果没有任务等待p_sem,才删除p_sem。
OS_OPT_DEL_ALWAYS 必须删除p_sem。

错误:
OS_ERR_NONE 没错误。
OS_ERR_DEL_ISR 该函数在中断中被调用。
OS_ERR_OBJ_PTR_NULL p_sem 为空。
OS_ERR_OBJ_TYPE p_sem 不是多值信号量类型。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_TASK_WAITING 还有任务在等待该信号量。

返回:
0,没有任务在等待该信号量,或者有错误产生。
>0,信号量被删除前等待其的任务数。
*/
OS_OBJ_QTY OSSemDel (
OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err
);

/*
设置多值信号量的计数值。

错误:
OS_ERR_NONE 没错误。
OS_ERR_SET_ISR 函数在中断中被调用。
OS_ERR_OBJ_PTR_NULL p_sem 为空。
OS_ERR_OBJ_TYPE p_sem 不是多值信号量类型。
OS_ERR_TASK_WAITING 还有任务在等待p_sem。
*/
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
/*
创建一个互斥信号量。

错误:
OS_ERR_NONE 没错误
OS_ERR_CREATE_ISR 在中断中调用该函数
OS_ERR_ILLEGAL_CREATE_RUN_TIME 在调用OSSafetyCriticalStart() 函数后创建内核对象
OS_ERR_NAME p_name 为空指针
OS_ERR_OBJ_CREATED 信号量已经被创建过
OS_ERR_OBJ_PTR_NULL p_mutex 是个空指针
*/
void OSMutexCreate (
OS_MUTEX *p_mutex, // 互斥信号量指针。
CPU_CHAR *p_name, // 互斥信号量名称。
OS_ERR *p_err
);

/*
释放互斥信号量。

选项:
OS_OPT_POST_NONE 释放信号量后,如果信号量可用,而且有任务正在等待,就(默认)进行任务调度。
OS_OPT_POST_NO_SCHED 释放信号量后,如果信号量可用,而且有任务正在等待,不进行任务调度,继续运行当前任务。

错误:
OS_ERR_NONE 无错误。
OS_ERR_MUTEX_NESTING p_mutex 被嵌套。
OS_ERR_MUTEX_NOT_OWNER 当前任务不持有p_mutex。
OS_ERR_OBJ_PTR_NULL p_mutex 为空。
OS_ERR_OBJ_TYPE p_mutex 不是多值信号量类型。
OS_ERR_POST_ISR 在中断中释放多值信号量。
*/
void OSMutexPost (
OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err
);

/*
申请一个互斥信号量。

选项:
OS_OPT_PEND_BLOCKING 如果不能立即获得信号量,就堵塞当前任务,继续等待信号量。
OS_OPT_PEND_NON_BLOCKING 如果不能立即获得信号量,不堵塞当前任务,不继续等待信号量。

错误:
OS_ERR_NONE 没错误,获得信号量。
OS_ERR_OBJ_DEL p_sem 被删除。
OS_ERR_OBJ_PTR_NULL p_sem 为空。
OS_ERR_OBJ_TYPE p_sem 不是信号量类型对象。
OS_ERR_OPT_INVALID opt 非法。
OS_ERR_PEND_ABORT 等待被另一个任务中止。
OS_ERR_PEND_ISR 在中断中被调用。
OS_ERR_PEND_WOULD_BLOCK 缺乏堵塞。
OS_ERR_SCHED_LOCKED 调度器被锁。
OS_ERR_STATUS_INVALID 等待状态非法。
OS_ERR_TIMEOUT 等待超时。

返回:
0,有错误。
其他值,信号量的计数值。
*/
void OSMutexPend (
OS_MUTEX *p_mutex,
OS_TICK timeout, // 等待超时时间( 单位: 时钟节拍), 0 代表无期限等待。opt 为OS_OPT_PEND_BLOCKING 时该参数才起作用。
OS_OPT opt,
CPU_TS *p_ts, // 时间戳
OS_ERR *p_err
);

/*
中止对一个互斥信号量的等待。

选项:
OS_OPT_PEND_ABORT_1 只中止该信号量等待列表中的最高优先级任务。
OS_OPT_PEND_ABORT_ALL 中止该信号量等待列表中的所有优先级任务。
OS_OPT_PEND_ABORT_1 |OS_OPT_POST_NO_SCHED只中止该信号量等待列表中的最高优先级任务,但不进行任务调度。
OS_OPT_PEND_ABORT_ALL |OS_OPT_POST_NO_SCHED中止该信号量等待列表中的所有优先级任务,但不进行任务调度。


错误:
OS_ERR_NONE 没错误。
OS_ERR_OBJ_PTR_NULL p_mutex 为空。
OS_ERR_OBJ_TYPE p_mutex 不是多值信号量类型。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_PEND_ABORT_ISR 该函数在中断中被调用。
OS_ERR_PEND_ABORT_NONE 没有任务在等待该信号量。

返回:
0,没有任务在等待该信号量,或者有错误产生。
>0,被中止的任务数。
*/
OS_OBJ_QTY OSMutexPendAbort (
OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err
);

/*
删除一个互斥信号量。

选项:
OS_OPT_DEL_NO_PEND 如果没有任务等待p_mutex,才删除p_mutex。
OS_OPT_DEL_ALWAYS 必须删除p_mutex。

错误:
OS_ERR_NONE 无错误。
OS_ERR_DEL_ISR 该函数在中断中被调用。
OS_ERR_OBJ_PTR_NULL p_mutex 为空。
OS_ERR_OBJ_TYPE p_mutex 不是互斥信号量类型。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_STATE_INVALID 持有信号量任务状态非法。
OS_ERR_TASK_WAITING 还有任务在等待该信号量。

返回:
0,没有任务在等待该信号量,或者有错误产生。
>0,信号量被删除前等待其的任务数。
*/
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
/*
创建一个消息队列。

错误:
OS_ERR_NONE 无错误
OS_ERR_CREATE_ISR 在中断中调用该函数
OS_ERR_ILLEGAL_CREATE_RUN_TIME 在调用OSSafetyCriticalStart() 函数后创建内核对象
OS_ERR_NAME p_name 为空指针
OS_ERR_OBJ_CREATED 该消息队列已经被创建过
OS_ERR_OBJ_PTR_NULL p_q 是个空指针
OS_ERR_Q_SIZE max_qty 为0.
*/
void OSQCreate (
OS_Q *p_q, // 消息队列指针。
CPU_CHAR *p_name, // 消息队列名称。
OS_MSG_QTY max_qty, // 最大消息数目。
OS_ERR *p_err
);

/*
向消息队列发布一个消息。

选项:
OS_OPT_POST_FIFO 把消息发布到队列的入口端,并且只唤醒一个等待任务。
OS_OPT_POST_LIFO 把消息发布到队列的出口端,并且只唤醒一个等待任务。
OS_OPT_POST_FIFO |OS_OPT_POST_ALL 把消息发布到队列的入口端,并且唤醒全部等待任务。
OS_OPT_POST_LIFO |OS_OPT_POST_ALL 把消息发布到队列的出口端,并且唤醒全部
等待任务。
OS_OPT_POST_FIFO |OS_OPT_POST_NO_SCHED 把消息发布到队列的入口端;只唤醒一个等待任务;不进行任务调度,继续运行当前任务。
OS_OPT_POST_LIFO |OS_OPT_POST_NO_SCHED 把消息发布到队列的出口端;只唤醒一个等待任务;不进行任务调度,继续运行当前任

错误:
OS_ERR_NONE 调用成功,消息被发布了。
OS_ERR_MSG_POOL_EMPTY 消息池没可用消息。
OS_ERR_OBJ_PTR_NULL p_q 为空。
OS_ERR_OBJ_TYPE p_q 不是消息队列类型。
OS_ERR_Q_MAX 消息队列已满。
*/
void OSQPost (
OS_Q *p_q,
void *p_void, // 消息指针
OS_MSG_SIZE msg_size, // 消息大小(字节)
OS_OPT opt,
OS_ERR *p_err
);

/*
等待一个消息队列的消息。

选项:
OS_OPT_PEND_BLOCKING 如果不能立即获得消息,就堵塞当前任务,继续等待消息。
OS_OPT_PEND_NON_BLOCKING 如果不能立即获得消息,不堵塞当前任务,不继续等待消息。

错误:
OS_ERR_NONE 没错误,任务获得消息。
OS_ERR_OBJ_PTR_NULL p_q 为空。
OS_ERR_PTR_INVALID p_msg_size 为空。
OS_ERR_OBJ_TYPE p_q 不是消息队列类型对象。
OS_ERR_PEND_ABORT 等待被中止。
OS_ERR_PEND_ISR 在中断中被调用。
OS_ERR_PEND_WOULD_BLOCK 缺乏堵塞。
OS_ERR_SCHED_LOCKED 调度器被锁。
OS_ERR_STATUS_INVALID 等待状态非法。
OS_ERR_TIMEOUT 等待超时。

返回:
!= (void *)0,接收到的消息的指针(首地址)。
== (void *)0,接收到一个空消息,或者没接收到消息,或者所等待的消息队列不存在,或者用户传给p_q 的不是消息队列类型的对象。
*/
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_OPT_PEND_ABORT_1 只中止该消息队列等待列表中的最高优先级任务。
OS_OPT_PEND_ABORT_ALL 中止该消息队列等待列表中的所有优先级任务。
OS_OPT_PEND_ABORT_1 |OS_OPT_POST_NO_SCHED只中止该消息队列等待列表中的最高优先级任务,但不进行任务调度。
OS_OPT_PEND_ABORT_ALL |OS_OPT_POST_NO_SCHED中止该消息队列等待列表中的所有优先级任务,但不进行任务调度。

错误:
OS_ERR_NONE 无错误。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_OBJ_PTR_NULL p_q 为空。
OS_ERR_OBJ_TYPE p_q 不是消息队列类型。
OS_ERR_PEND_ABORT_ISR 该函数在中断中被调用。
OS_ERR_PEND_ABORT_NONE 没有任务在等待该消息队列。

返回:
0,没有任务在等待该信号量,或者有错误产生。
>0,被中止的任务数。
*/
OS_OBJ_QTY OSQPendAbort (
OS_Q *p_q,
OS_OPT opt,
OS_ERR *p_err
);

/*
删除一个消息队列。

选项:
OS_OPT_DEL_NO_PEND 如果没有任务等待p_q,才删除p_q。
OS_OPT_DEL_ALWAYS 必须删除p_q。

错误:
OS_ERR_NONE 无错误。
OS_ERR_DEL_ISR 该函数在中断中被调用。
OS_ERR_OBJ_PTR_NULL p_q 为空。
OS_ERR_OBJ_TYPE p_q 不是消息队列类型。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_TASK_WAITING 还有任务在等待该消息队列。

返回:
0,没有任务在等待该消息队列,或者有错误产生。
>0,消息队列被删除前等待其的任务数。
*/
OS_OBJ_QTY OSQDel (
OS_Q *p_q,
OS_OPT opt,
OS_ERR *p_err
);

/*
清空一个消息队列(的消息)。

错误:
OS_ERR_NONE 无错误,执行成功。
OS_ERR_FLUSH_ISR 该函数在中断中被调用。
OS_ERR_OBJ_PTR_NULL p_q 为空。
OS_ERR_OBJ_TYPE p_q 不是消息队列类型。

返回:
清空消息队列前队列里的消息数目。
*/
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
/*
创建一个事件标志组。

错误:
OS_ERR_NONE 无错误,创建成功。
OS_ERR_CREATE_ISR 在中断中调用该函数
OS_ERR_ILLEGAL_CREATE_RUN_TIME 在调用OSSafetyCriticalStart() 函数后创建内核对象
OS_ERR_NAME p_name 为空指针
OS_ERR_OBJ_CREATED 该事件标志组已经被创建过
OS_ERR_OBJ_PTR_NULL p_q 是个空指针
*/
void OSFlagCreate (
OS_FLAG_GRP *p_grp, // 事件标志组指针。
CPU_CHAR *p_name, // 事件标志组名称。
OS_FLAGS flags, // 事件标志初始值。
OS_ERR *p_err
);


/*
发布一个事件标志组。

选项:
OS_OPT_POST_FLAG_SET 把选定的标志位置1。
OS_OPT_POST_FLAG_CLR 把选定的标志位清0。
OS_OPT_POST_FLAG_SET |OS_OPT_POST_NO_SCHED 把消息发布到队列的末端,并且唤醒全部等待任务;不进行任务调度,继续运行当前任务。
OS_OPT_POST_FLAG_CLR |OS_OPT_POST_NO_SCHED 把消息发布到队列的前端,并且唤醒全部等待任务;不进行任务调度,继续运行当前任务。

错误:
OS_ERR_NONE 调用成功。
OS_ERR_OBJ_PTR_NULL p_grp 为空。
OS_ERR_OBJ_TYPE p_grp 不是事件标志组类型。
OS_ERR_OPT_INVALID 选项非法。

返回:
事件标志组的标志值。
*/
OS_FLAGS OSFlagPost (
OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_OPT opt,,
OS_ERR *p_err
);

/*
等待一个事件标志组的事件组合发生。

选项:
OS_OPT_PEND_FLAG_CLR_ALL 等待flags 指定位均被清0。
OS_OPT_PEND_FLAG_CLR_ANY 等待flags 指定位有一位被清0 即可。
OS_OPT_PEND_FLAG_SET_ALL 等待flags 指定位均被置1。
OS_OPT_PEND_FLAG_SET_ANY 等待flags 指定位有一位被置1 即可。
OS_OPT_PEND_FLAG_CLR_ALL |OS_OPT_PEND_FLAG_CONSUME 等待flags 指定位均被清0;等到后把触发位取反。
OS_OPT_PEND_FLAG_CLR_ANY |OS_OPT_PEND_FLAG_CONSUME 等待flags 指定位有一位被清0 即可;等到后把触发位取反。
OS_OPT_PEND_FLAG_SET_ALL |OS_OPT_PEND_FLAG_CONSUME等待flags 指定位均被置1;等到后把触发位取反。
OS_OPT_PEND_FLAG_SET_ANY |OS_OPT_PEND_FLAG_CONSUME等待flags 指定位有一位被置1 即可;等到后把触发位取反。
OS_OPT_PEND_FLAG_CLR_ALL |OS_OPT_PEND_NON_BLOCKING要求flags 指定位均被清0;不符合要求不等待。
OS_OPT_PEND_FLAG_CLR_ANY |OS_OPT_PEND_NON_BLOCKING要求flags 指定位有一位被清0;不符合要求不等待。
OS_OPT_PEND_FLAG_SET_ALL |OS_OPT_PEND_NON_BLOCKING要求flags 指定位均被置1;不符合要求不等待。
OS_OPT_PEND_FLAG_SET_ANY |OS_OPT_PEND_NON_BLOCKING要求flags 指定位有一位被置1;不符合要求不等待。
OS_OPT_PEND_FLAG_CLR_ALL |OS_OPT_PEND_FLAG_CONSUME |OS_OPT_PEND_NON_BLOCKING要求flags 指定位均被清0;符合要求就把触发位取反;不符合要求不等待;。
OS_OPT_PEND_FLAG_CLR_ANY |OS_OPT_PEND_FLAG_CONSUME |OS_OPT_PEND_NON_BLOCKING要求flags 指定位有一位被清0;符合要求就把触发位取反;不符合要求不等待;。
OS_OPT_PEND_FLAG_SET_ALL |OS_OPT_PEND_FLAG_CONSUME |OS_OPT_PEND_NON_BLOCKING要求flags 指定位均被置1;符合要求就把触发位取反;不符合要求不等待;。
OS_OPT_PEND_FLAG_SET_ANY |OS_OPT_PEND_FLAG_CONSUME |OS_OPT_PEND_NON_BLOCKING要求flags 指定位有一位被置1;符合要求就把触发位取反;不符合要求不等待;。

错误:
OS_ERR_NONE 等待成功,指定事件组合发生。
OS_ERR_OBJ_PTR_NULL p_grp 为空。
OS_ERR_OBJ_TYPE p_grp 不是事件标志组类型对象。
OS_ERR_PEND_ABORT 等待被中止。
OS_ERR_PEND_ISR 在中断中被调用。
OS_ERR_PEND_WOULD_BLOCK 缺乏堵塞。
OS_ERR_SCHED_LOCKED 调度器被锁。
OS_ERR_TIMEOUT 等待超时。

返回:
!= (void *)0,任务脱离等待时的事件标志组的标志成员值。
== (void *)0,有错误,或者等待超时。
*/
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_OPT_PEND_ABORT_1 只中止该事件标志组等待列表中的最高优先级任务。
OS_OPT_PEND_ABORT_ALL 中止该事件标志组等待列表中的所有优先级任务。
OS_OPT_PEND_ABORT_1 |OS_OPT_POST_NO_SCHED只中止该事件标志组等待列表中的最高优先级任务,但不进行任务调度。
OS_OPT_PEND_ABORT_ALL |OS_OPT_POST_NO_SCHED中止该事件标志组等待列表中的所有优先级任务,但不进行任务调度。

错误:
OS_ERR_NONE 无错误。
OS_ERR_OBJ_PTR_NULL p_grp 为空
OS_ERR_OBJ_TYPE p_grp 不是事件标志组类型。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_PEND_ABORT_ISR 该函数在中断中被调用。
OS_ERR_PEND_ABORT_NONE 没有任务在等待该事件标志组。

返回:
0,没有任务在等待该事件标志组,或者有错误产生。
>0,被中止的任务数。
*/
OS_OBJ_QTY OSFlagPendAbort (
OS_FLAG_GRP *p_grp,
OS_OPT opt,
OS_ERR *p_err
);

/*
删除一个事件标志组。

选项:
OS_OPT_DEL_NO_PEND 如果没有任务等待p_grp,才删除p_grp。
OS_OPT_DEL_ALWAYS 必须删除p_grp。

错误:
OS_ERR_NONE 无错误。
OS_ERR_DEL_ISR 该函数在中断中被调用。
OS_ERR_OBJ_PTR_NULL p_grp 为空。
OS_ERR_OBJ_TYPE p_grp 不是事件标志组类型。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_TASK_WAITING 还有任务在等待该事件标志组。

返回
0,没有任务在等待该事件标志组,或者有错误产生。
>0,事件标志组被删除前等待其的任务数。
*/
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_OPT_PEND_BLOCKING 如果目前没有对象已被发布,阻塞任务,等待对象被发布。
OS_OPT_PEND_NON_BLOCKING 如果目前没有对象已被发布,不阻塞任务。
*/
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_OPT_POST_NONE 没有选项。
OS_OPT_POST_NO_SCHED 不进行任务调度。

错误:
OS_ERR_NONE 没错误
OS_ERR_SEM_OVF 该发布将导致了信号量的计数值溢出。
OS_ERR_STATE_INVALID 任务状态非法。

返回:
0,有错误。
其他值,任务信号量的当前计数值。
*/
OS_SEM_CTR OSTaskSemPost (
OS_TCB *p_tcb, // 目标任务控制块。
OS_OPT opt,
OS_ERR *p_err
);

/*
等待任务信号量。

选项:
OS_OPT_PEND_BLOCKING 如果不能立即获得信号量,就堵塞当前任务,继续等待信号量。
OS_OPT_PEND_NON_BLOCKING 如果不能立即获得信号量,不堵塞当前任务,不继续等待信号量。

错误:
OS_ERR_NONE 没错误,成功获得信号量。
OS_ERR_PEND_ABORT 等待被另一个任务中止。
OS_ERR_PEND_ISR 在中断中被调用。
OS_ERR_PEND_WOULD_BLOCK 缺乏堵塞。
OS_ERR_SCHED_LOCKED 调度器被锁。
OS_ERR_STATUS_INVALID 等待状态非法。
OS_ERR_TIMEOUT 等待超时。

返回:
0,信号量的当前计数值为0,或有错误。
其他值,信号量的当前计数值。
*/
OS_SEM_CTR OSTaskSemPend (
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
CPU_TS *p_ts,
OS_ERR *p_err
);

/*
中止一个任务对其任务信号量的等待。

选项:
OS_OPT_POST_NONE 没有选项。
OS_OPT_POST_NO_SCHED 不进行任务调度。

错误:
OS_ERR_NONE 没错误,成功中止。
OS_ERR_PEND_ABORT_ISR 该函数在中断中被调用。
OS_ERR_PEND_ABORT_NONE 目标任务并未在等待任务信号量。
OS_ERR_PEND_ABORT_SELF 目标任务是自身。

返回:
== DEF_FALSE>0,目标任务没在等待任务信号量,或者有错误。
== DEF_TRUE,目标任务确实在等待任务信号量,而且等待成功被中止。

目标任务不可以是自身(当前运行任务)。
*/
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
/*
向任务消息队列发布一个消息。

选项:
OS_OPT_POST_FIFO 把消息发布到队列的入口端。
OS_OPT_POST_LIFO 把消息发布到队列的出口端。
OS_OPT_POST_FIFO |OS_OPT_POST_NO_SCHED把消息发布到队列的入口端;不进行任务调度,继续运行当前任务。
OS_OPT_POST_LIFO |OS_OPT_POST_NO_SCHED把消息发布到队列的出口端;不进行任务调度,继续运行当前任务。

错误:
OS_ERR_NONE 无错误,消息成功被发布了。
OS_ERR_Q_MAX 任务消息队列已满。
OS_ERR_MSG_POOL_EMPTY 消息池没可用消息。
*/
void OSTaskQPost (
OS_TCB *p_tcb, // 目标任务。如果该参数为NULL,消息将发送给当前运行任务。
void *p_void, // 消息指针。
OS_MSG_SIZE msg_size, // 消息长度。
OS_OPT opt,
OS_ERR *p_err
);


/*
等待任务消息队列的消息。

选项:
OS_OPT_PEND_BLOCKING 如果不能立即获得消息,就堵塞当前任务,继续等待消息。
OS_OPT_PEND_NON_BLOCKING 如果不能立即获得消息,不堵塞当前任务,不继续等待消息。

错误:
OS_ERR_NONE 没错误,任务成功获得消息。
OS_ERR_PTR_INVALID p_msg_size 为空。
OS_ERR_PEND_ABORT 等待被中止。
OS_ERR_PEND_ISR 在中断中被调用。
OS_ERR_PEND_WOULD_BLOCK 缺乏堵塞。
OS_ERR_Q_EMPTY 任务消息队列里没有消息
OS_ERR_SCHED_LOCKED 调度器被锁。
OS_ERR_TIMEOUT 等待超时。

返回:
!= NULL,接收到的消息的指针(首地址)。
== NULL,接收到一个空消息,或者有错误。
*/
void *OSTaskQPend (
OS_TICK timeout,
OS_OPT opt,
OS_MSG_SIZE *p_msg_size, // 消息长度。
CPU_TS *p_ts,
OS_ERR *p_err
);

/*
中止一个任务对其消息队列的等待。

选项:
OS_OPT_POST_NONE 没有选项要求。
OS_OPT_POST_NO_SCHED 不进行任务调度。

错误:
OS_ERR_NONE 无错误,中止成功。
OS_ERR_OPT_INVALID 选项非法。
OS_ERR_PEND_ABORT_ISR 该函数在中断中被调用。
OS_ERR_PEND_ABORT_NONE 目标任务没在等待任务消息队列。
OS_ERR_PEND_ABORT_SELF 目标任务是自身(当前运行任务)。

返回:
== DEF_FALSE,目标任务没在等待任务消息队列,或有错误。
== DEF_TRUE,目标任务是在等待任务消息队列,而且等待被中止。

p_tcb 不能为NULL 或当前运行任务。
*/
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
/*
创建一个内存管理对象。

错误:
OS_ERR_NONE 无错误,创建成功。
OS_ERR_CREATE_ISR 在中断中调用该函数
OS_ERR_ILLEGAL_CREATE_RUN_TIME 在调用OSSafetyCriticalStart() 函数后创建内核对象
OS_ERR_MEM_INVALID_BLKS 内存块数目非法。
OS_ERR_MEM_INVALID_P_ADDR 内存分区地址非法。
OS_ERR_MEM_INVALID_SIZE 内存空间大小非法。
*/
void OSMemCreate (
OS_MEM *p_mem, // 内存管理对象。
CPU_CHAR *p_name, // 命名内存管理对象。
void *p_addr, // 内存分区首地址。
OS_MEM_QTY n_blks, // 内存块数目,要求不小于2。
OS_MEM_SIZE blk_size, // 内存块空间字节数,不少于一个指针的字节数。(STM32 是的指针的字节数是4)。
OS_ERR *p_err
);

/*
向内存管理对象获取一个空闲内存块。

错误:
OS_ERR_NONE 无错误,获取成功。
OS_ERR_MEM_INVALID_P_MEM p_mem 为空。
OS_ERR_MEM_NO_FREE_BLKS 没有空闲的内存块。

返回:
获取到的内存块。
*/
void *OSMemGet (
OS_MEM *p_mem,
OS_ERR *p_err
);

/*
内存块退还回内存管理对象。

错误:
OS_ERR_NONE 无错误,退还成功。
OS_ERR_MEM_FULL 内存分区的空闲内存块已满。
OS_ERR_MEM_INVALID_P_BLK p_blk 为空。
OS_ERR_MEM_INVALID_P_MEM p_mem 为空。
*/
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
/*
创建一个任务。

选项:
OS_OPT_TASK_NONE 没有选项要求。
OS_OPT_TASK_STK_CHK允许任务进行堆栈检测。
OS_OPT_TASK_STK_CLR堆栈全部进行清0 初始化。
OS_OPT_TASK_SAVE_FP在上下文切换时保存浮点寄存器。STM32 芯片没有浮点寄存器,该项一般不用。
OS_OPT_TASK_NO_TLS屏蔽任务的TLS 支持。

错误:
OS_ERR_NONE 无错误,创建成功。
OS_ERR_ILLEGAL_CREATE_RUN_TIME在调用OSSafetyCriticalStart()函数后创建内核对象
OS_ERR_NAME p_name 为空。
OS_ERR_PRIO_INVALID prio>= S_CFG_PRIO_MAX-1,或者在
OS_CFG_ISR_POST_DEFERRED_EN 被置1 时prio=0。
OS_ERR_STK_SIZE_INVALID p_stk_base 为空。
OS_ERR_STK_LIMIT_INVALID stk_limit 大于stk_size。
OS_ERR_TASK_CREATE_ISR 在中断中创建任务。
OS_ERR_TASK_INVALID p_task 为空。
OS_ERR_TCB_INVALID p_tcb 为空。
*/
void OSTaskCreate (
OS_TCB *p_tcb, // 任务控制块指针。
CPU_CHAR *p_name, // 命名任务。
OS_TASK_PTR p_task, // 任务函数。
void *p_arg, // 传递给任务函数的参数。
OS_PRIO prio, // 任务优先级。uC/OS-III 允许任务拥有相同的优先级。
CPU_STK *p_stk_base, // 任务堆栈指针。
CPU_STK_SIZE stk_limit, // 任务堆栈的限制空间。
CPU_STK_SIZE stk_size, // 任务堆栈总空间。
OS_MSG_QTY q_size, // 任务消息队列容量。只有使能了任务消息队列,该参数才有效。
OS_TICK time_quanta, // 时间片(单位:时钟节拍)。如果该参数设为0,表示使用系统默认值
( OSCfg_TickRate_Hz / 10)。
void *p_ext, // 任务扩展内容的指针。
OS_OPT opt,
OS_ERR *p_err
);


/*
挂起一个任务。

错误:
OS_ERR_NONE 无错误,挂起任务成功。
OS_ERR_SCHED_LOCKED 挂起当前任务时调度器被锁。
OS_ERR_TASK_SUSPEND_ISR 在禁用中断延迟发布的情况下,在中断中调用该函数。
OS_ERR_TASK_SUSPEND_IDLE 挂起空闲任务。
OS_ERR_TASK_SUSPEND_INT_HANDLER 在使能中断延迟发布的情况下,挂起中断延迟提交任务。

不能挂起空闲任务。
当OS_CFG_ISR_POST_DEFERRED_EN = 0u 时,不能在中断中调用该函数。
当OS_CFG_ISR_POST_DEFERRED_EN > 0u 时,不能挂起中断延迟提交任务。
*/
void OSTaskSuspend (
OS_TCB *p_tcb, // 任务控制块指针,0 表自身。
OS_ERR *p_err
);


/*
解嵌一个被挂起的任务。

错误:
OS_ERR_NONE 无错误,解嵌成功。
OS_ERR_STATE_INVALID 任务状态非法。
OS_ERR_TASK_RESUME_ISR 在禁用中断延迟发布的情况下,在中断中调用该函数。
OS_ERR_TASK_RESUME_SELF 解嵌自身。
OS_ERR_TASK_NOT_SUSPENDED 任务p_tcb 未被挂起。

不能解嵌自身,即p_tcb 不能为0 或当前运行任务。
当OS_CFG_ISR_POST_DEFERRED_EN = 0u 时,不能在中断中调用该函数。
*/
void OSTaskResume (
OS_TCB *p_tcb,
OS_ERR *p_err
);



/*
改变一个任务的优先级。

错误:
OS_ERR_NONE 无错误,调用成功。
OS_ERR_PRIO_INVALID 当使能了中断延迟发布时,prio_new = 0;或者,prio_new >=(OS_CFG_PRIO_MAX-1)。
OS_ERR_STATE_INVALID 目标任务的任务状态非法。
OS_ERR_TASK_CHANGE_PRIO_ISR 在中断中调用该函数。

当OS_CFG_ISR_POST_DEFERRED_EN > 0u 时,prio_new 不能为0;prio_new 不能>=(OS_CFG_PRIO_MAX-1)。
不能在中断中调用该函数。
*/
void OSTaskChangePrio (
OS_TCB *p_tcb,
OS_PRIO prio_new,
OS_ERR *p_err
);


/*
删除一个任务。

错误:
OS_ERR_NONE 无错误,调用成功。
OS_ERR_STATE_INVALID 任务状态非法。
OS_ERR_TASK_DEL_IDLE p_tcb 为空闲任务。
OS_ERR_TASK_DEL_INVALID 当使能了中断延迟发布时,p_tcb 为中断延迟提交任务。
OS_ERR_TASK_DEL_ISR 在中断中调用该函数。

当OS_CFG_ISR_POST_DEFERRED_EN > 0u 时,不能删除中断延迟提交任务。
*/
void OSTaskDel (
OS_TCB *p_tcb,
OS_ERR *p_err
);

/*
配置时间片轮转调度。

当OS_CFG_ISR_POST_DEFERRED_EN = 0u 时,不能在中断中调用该函数。
*/
void OSSchedRoundRobinCfg (
CPU_BOOLEAN en, // 使能 DEF_ENABLED/禁用 DEF_DISABLED事件片轮转调度
OS_TICK dflt_time_quanta, // 设置默认时间片:>0 把dflt_time_quanta 设为默认时间片值;=0 把系统默认值OSCfg_TickRate_Hz / 10 设为默认时间片值。
OS_ERR *p_err
);


/*
放弃时间片。

错误:
OS_ERR_NONE 无错误,调用成功。
OS_ERR_ROUND_ROBIN_1 当前优先级的就绪列表中只有一个任务(当前任务)。
OS_ERR_ROUND_ROBIN_DISABLED 未使能时间片轮转调度。
OS_ERR_SCHED_LOCKED 调度器被锁。
OS_ERR_YIELD_ISR 在中断中调用该函数。
*/
void OSSchedRoundRobinYield (
OS_ERR *p_err
);


/*
设置一个任务的时间片。

错误:
OS_ERR_NONE 无错误,调用成功。
OS_ERR_SET_ISR 在中断中调用该函数。
*/
void OSTaskTimeQuantaSet (
OS_TCB *p_tcb,
OS_TICK time_quanta,
OS_ERR *p_err
);

/*
设置一个任务的某个任务寄存器的值。

错误:
OS_ERR_NONE 无错误,调用成功。
OS_ERR_REG_ID_INVALID Id 不在[ 0,OS_CFG_TASK_REG_TBL_SIZE – 1 ]内。
*/
void OSTaskRegSet (
OS_TCB *p_tcb,
OS_REG_ID id, // 任务寄存器的id,取值范围为[ 0,OS_CFG_TASK_REG_TBL_SIZE – 1 ]。
OS_REG value, // 设置任务寄存器的内容。
OS_ERR *p_err
);


/*
获取一个任务的某个任务寄存器的值。

返回:
0,获取的任务寄存器的值为0(p_err == OS_ERR_NONE),或有错误(p_err !=
OS_ERR_NONE)。
其他,获取的任务寄存器的值。
*/
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 时钟运行的计数值,通过BSP_CPU_ClkFreq() 函数可以获取CPU 时钟频率。
*/
CPU_TS_TMR CPU_IntDisMeasMaxCurGet (void)



统计信息

统计信息的开关在:os_cfg.h\OS_CFG_STAT_TASK_EN

CPU 使用率和其最大记录分别保存于全局变量OSStatTaskCPUUsageOSStatTaskCPUUsageMax,一个任务的CPU 使用率和其最大记录分别保存于其任务控制块的CPUUsageCPUUsageMax 成员,一个任务的任务堆栈的空闲大小和已用大小分别保存于其任务控制块的StkFreeStkUsed 成员。需要注意,OSStatTaskCPUUsageOSStatTaskCPUUsageMaxCPUUsageCPUUsageMax 均被放大了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 主频。

返回:
CPU 的主频(单位:HZ),即HCLK 时钟频率。
*/
CPU_INT32U BSP_CPU_ClkFreq (void);

/*
获取系统版本号。

返回:
0,有错误,获取uC/OS 系统版本号失败。
其他值,uC/OS 系统版本号。

返回的版本号是一个被去掉“.”符号的整数,用户需要自行处理获取真实的版本
号。如版本号为“V3.01.02”就返回30102
*/
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()      // 创建任务 OS_TASK_CREATE_EN      
OSTaskCreateExt() // 创建任务 OS_TASK_CREATE_EXT_EN
OSTaskStkChk() // 堆栈检验 OS_TASK_CREATE_EXT_EN
OSTaskDel() // 删除任务 OS_TASK_DEL_EN
OSTaskDelReq() // 请求删除任务 OS_TASK_DEL_EN
OSTaskSuuspend() // 挂起任务 OS_TASK_SUSPEND_EN
OSTaskResume() // 恢复任务 OS_TASK_SUSPEND_EN
OSTaskChangePrio() // 改变优先级 OS_TASK_CHANGE_PRIO_EN
OSTaskQuery() // 查询任务信息 OS_TASK_QUERY_EN

创建任务要求:

  • 创建任务不能在中断中创建;
  • 且优先级范围是0~63,每个优先级与每个任务一一对应;
  • 系统在运行期间可以创建任务。

创建任务:

1
2
3
4
5
6
7
8
9
10
/*
void (*task)(void *p_arg):任务函数指针
void *p_arg:堆栈指针
OS_STK *ptos:堆栈栈顶指针
INT8U prio:任务优先级

返回值:返回函数错误信息
如果没有错误,返回OS_NO_ERR
*/
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 prio:优先级

// 删除任务
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)
{
// Code
OSInit(); // 系统初始化
// Code
OSTaskCreate(Task, args); // 创建任务
OSTaskCreate(Task, args); // 创建任务
// Code
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(); // 开中断
}
}

// 统计任务,用于统计CPU使用率:OSCPUsage
// 开启需要在OS_CFG.H中配置OS_TASK_STAT_EN 为 1
// 且需要在初始化 OSStatInit()
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() // 设置分区名称