RTOSv2 Project Guide
Start a New RTOS v2 Project
首先,在这里说明一下,RTOS的官方手册,在环境管理中可以很直接通过点击文件来直达,不过在这里再列一下网址:http://www.keil.com/pack/doc/CMSIS/Driver/html/group__usart__interface__gr.html#details
同keil新建工程的工程一样,先准备选择一个文件夹来保存工程。
选择合适的MCU型号
然后,环境管理界面弹出,分别选择CMSIS – > CORE;CMSIS – > RTOS(API)–> Keil RTX5 ;CMSIS – > RTOS2(API)–> Keil RTX5(右边的Variant 选择Library);Device --> GPIO+Startup; Device --> StdPeriph Drivers --> EXTI+Framework+GPIO+RCC+USART+TIM+IWDG+WWDG(这是几个常用的,以后添加可以按需添加,以后也可以随时添加);CMSIS Drivers --> USART(API)–>USART;如下所示。接下来点击确定,新的工程就建好了。
点击左边的文件树列表,如下图。
接下来进入工程根目录下,建一个文件夹(这里为Custom)用来保存我们自己的代码。
接下来,将我们已经写好的代码直接搬过来。主要是main.c文件和开发板上按键和led的代码,boardled.c, boardled.h, boardkey.c, boardkey.h。这些文件分别是附录中的文件1,2,3,4,5。
然后,回到IDE工程中,开始将这些文件添加到工程中。
进入Custom文件夹,将下面的文件类型选择为所有文件类型,这样就可以将.h文件也添加到工程中,在这里添加头文件并不能让它们可以被工程使用,这里添加头文件的目的是,这样可以直接在IDE的工程文件栏中显示,直接打开编辑,方便用户使用。
如上所言,让工程使用头文件还需要在option中那个设置。
按照如下步骤一次,1. 选择C/C++菜单栏,2. 点击包含路径,3. 添加路径选项,4. 浏览目录 ,5. 选择有头文件的文件夹,6. 点击选择文件夹。
顺便,如下依次配置好option,这在前面的教程中已经做过了,不再详述。
代码在后面的附录中,其功能是让板上的KEY 1控制 LED1,KEY 2控制 LED2,按键按下,明灭状态就变换一次。这是一个很简单的例子。
重建所有的目标文件后,下载到板上。
板子的接线和效果如下所示:
2. Config USART and Use USART API
RTOS中USART的配置已经很容易操作了,但是为了满足正常的需求,作者这里有添加了一些小函数,有些函数只有其中一个USART有,若读者需要可以自行修改添加。文件为serial.h和serial.c,见附录文件
首先,将用到的文件,serial.c serial.h 见附录代码7和附录代码8添加到工程中,main.c的修改为附录6中的代码, 代码中也同时包含了配置了新线程微信线程分配资源的示例。
再次进入环境管理界面,添加如下两个库文件,点击OK。
然后,比较重要的事情来了,在RTOS中要正常使用其驱动API还需要做一些额外设置,去打开这些外设,配置文件为RTE_Device.h 。
进入该文件后,是看一看到定义了很多内容,但我们想要是用什么并不清楚,但是,可以看到代码界面下方有两个选项,直接切入Configuration Wizard。
进去会发现,突然有种豁然开朗的感觉。可以看到时钟已经配置完毕。然后点击+,配置USART1和USART2。记得要保存。配置如下:
接下来,将程序重建,下载到开发板上。使用USB转串口工具,将USART1跟电脑连接起来。打开串口调试工具,找到正确的端口,将波特率设置为9600,发送AT+CGATT?加回车(实际发送: “AT+CGATT?\r\n”)。main.c 中的代码,在m5310模组和电脑之间建立虚拟链接。所以会收到如下所示内容。
附录
- Filename: main.c
/*----------------------------------------------------------------------------
Designers Guide to the Cortex-M Family
CMSIS RTOS Threads Example
*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
Include the microcontroller header for register defenitions and CMSIS core functions
Include the CMSIS RTOS header for the RTOS API
*---------------------------------------------------------------------------*/
//#include "STM32F10x.h"
#include <cmsis_os2.h>
#include <cmsis_os.h>
#include "boardled.h"//板上LED
#include "boardkey.h"//板上按键
//按键事件,在boardkey中声明且调用,当有按键事件,调用(执行)此函数
//这里执行的内容是按下按键,对应LED的亮灭状态变换一下。LED1对应KEY1,LED2对应KEY2。
void KEY_Event(uint8_t num){
static uint8_t falg01 = 1;
static uint8_t falg02 = 1;
if(num == 4){
if(falg01){
LED_On(0);
falg01 = 0;
}
else{
LED_Off(0);
falg01 = 1;
}
}
else if(num == 5)
{
if(falg02){
LED_On(1);
falg02 = 0;
}
else{
LED_Off(1);
falg02 = 1;
}
}
}
//线程1,执行LED和按键的初始化后挂起
void thread1 (void *argument)
{
LED_Initialize ();
KEY_Initialize ();
osSignalWait(0x01, osWaitForever);
}
//系统初始化,开始一个新线程
int main (void)
{
SystemCoreClockUpdate();
osKernelInitialize (); // initialize CMSIS-RTOS
osThreadNew(thread1,NULL,NULL);
osKernelStart(); // start thread execution
while(1);
}
- Filename: boardled.h
#ifndef __BOARDLED_H
#define __BOARDLED_H
#include "GPIO_STM32F10x.h"
int32_t LED_Initialize (void);
int32_t LED_Uninitialize (void);
int32_t LED_On (uint32_t num);
int32_t LED_Off (uint32_t num);
int32_t LED_SetOut (uint32_t val);
uint32_t LED_GetCount (void);
void thread_keyrun (void *argument);
int32_t KEY_Initialize (void);
#endif /*__BOARDLED_H*/
- Filename: boardled.c
#include "boardled.h"
#include <cmsis_os.h>
/*There is 2 LED , LDE1 is ctrolled by PB0, LDE2 ctrolled by PB1*/
const GPIO_PIN_ID Pin_LED[] = { //引脚描述结构体
{GPIOB, 0},
{GPIOB, 1},
};
#define LED_COUNT (sizeof(Pin_LED)/sizeof(GPIO_PIN_ID))
int32_t LED_Initialize (void) { //使用RTOS API配置GPIO
uint32_t n;
/* Configure pins: Push-pull Output Mode (50 MHz) with Pull-down resistors */
for (n = 0; n < LED_COUNT; n++) {
GPIO_PortClock (Pin_LED[n].port, true);
GPIO_PinWrite (Pin_LED[n].port, Pin_LED[n].num, 0);
GPIO_PinConfigure(Pin_LED[n].port, Pin_LED[n].num,
GPIO_OUT_PUSH_PULL,
GPIO_MODE_OUT2MHZ);
}
return 0;
}
int32_t LED_Uninitialize (void) {
uint32_t n;
/* Configure pins: Input mode, without Pull-up/down resistors */
for (n = 0; n < LED_COUNT; n++) {
GPIO_PinConfigure(Pin_LED[n].port, Pin_LED[n].num,
GPIO_IN_FLOATING,
GPIO_MODE_INPUT);
}
return 0;
}
int32_t LED_On (uint32_t num) { //开LED灯,如果超出操作范围,返回错误值-1
int32_t retCode = 0;
if (num < LED_COUNT) {
GPIO_PinWrite(Pin_LED[num].port, Pin_LED[num].num, 1);
}
else {
retCode = -1;
}
return retCode;
}
int32_t LED_Off (uint32_t num) {
int32_t retCode = 0;
if (num < LED_COUNT) {
GPIO_PinWrite(Pin_LED[num].port, Pin_LED[num].num, 0);
}
else {
retCode = -1;
}
return retCode;
}
int32_t LED_SetOut (uint32_t val) {
uint32_t n;
for (n = 0; n < LED_COUNT; n++) {
if (val & (1<<n)) {
LED_On (n);
} else {
LED_Off(n);
}
}
return 0;
}
uint32_t LED_GetCount (void) {
return LED_COUNT;
}
- Filename: boardkey.h
/*由于RTOS中没有外部中断的API,因此这里直接使用标准库的函数来配置*/
#ifndef __BOARDKEY_H
#define __BOARDKEY_H
#include "GPIO_STM32F10x.h"
#include "stm32f10x_exti.h"
#include <cmsis_os.h>
int32_t KEY_Initialize (void);
#endif /*__BOARDKEY_H*/
- Filename: boardkey.c
/*由于RTOS中没有外部中断的API,因此这里直接使用标准库的函数来配置*/
#include "boardkey.h"
extern void KEY_Event(uint8_t num);
int32_t KEY_Initialize (void) {
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE);//打开GPIO AFIO的时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource4); //选择EXTI信号源
EXTI_ClearITPendingBit(EXTI_Line4);
EXTI_InitStructure.EXTI_Line = EXTI_Line4; //中断线选择
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //EXTI为中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断
EXTI_Init(&EXTI_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource5); //选择EXTI信号源
EXTI_ClearITPendingBit(EXTI_Line5);
EXTI_InitStructure.EXTI_Line = EXTI_Line5; //中断线选择
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //配置NVIC优先级分组为1
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //中断源stm32f10x.h”中
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级:1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级:1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_Init(&NVIC_InitStructure);
return 0;
}
//中断服务函数的名称要特别小心,因为你随便写错一点,编译不会产生报错,但是会导致程序无法进入中断服务程序,甚至卡死。不确定的话可以到启动文件startup_stm32f10x_md.s中查找。
void EXTI4_IRQHandler(void) //按键1,PC4中断服务函数
{
if(EXTI_GetITStatus(EXTI_Line4)!= RESET)
{
EXTI_ClearITPendingBit(EXTI_Line4);
KEY_Event(4);
}
}
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line5)!= RESET)
{
EXTI_ClearITPendingBit(EXTI_Line5);
KEY_Event(5);
}
}
- Filename: main.c
/*----------------------------------------------------------------------------
Designers Guide to the Cortex-M Family
CMSIS RTOS Threads Example
*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
Include the microcontroller header for register defenitions and CMSIS core functions
Include the CMSIS RTOS header for the RTOS API
*---------------------------------------------------------------------------*/
//#include "STM32F10x.h"
#include <cmsis_os2.h>
#include <cmsis_os.h>
#include "boardled.h"
#include "boardkey.h"
#include "serial.h"
static uint64_t thread1_stk[128];
static uint64_t thread1_cdm[128];
void KEY_Event(uint8_t num){
static uint8_t falg01 = 1;
static uint8_t falg02 = 1;
if(num == 4){
if(falg01){
LED_On(0);
falg01 = 0;
}
else{
LED_Off(0);
falg01 = 1;
}
}
else if(num == 5)
{
if(falg02){
LED_On(1);
falg02 = 0;
}
else{
LED_Off(1);
falg02 = 1;
}
}
}
void thread1 (void *argument)
{
KEY_Initialize ();
LED_Initialize ();
osSignalWait(0x01, osWaitForever);
}
void thread2 (void *argument)
{
//osSignalWait(0x01, osWaitForever);
char temp1;
char temp2;
Serial1_Conf(9600);
Serial2_Conf(9600);
Serial1_Print("Usart1 Initialize Finished!\r\n");
osDelay(100);
while(1){
if(USART1_RecNum()){
USART2_SendByte(USART1_ReadByte());
}
if(USART2_RecNum()){
USART1_SendByte(USART2_ReadByte());
}
}
}
static osThreadAttr_t thread2_attr;
int main (void)
{
thread2_attr.stack_mem = thread1_stk;
thread2_attr.stack_size = sizeof(thread1_stk);
//thread1_attr.priority = osPriorityHigh;
thread2_attr.cb_mem = thread1_cdm;
thread2_attr.cb_size = sizeof(thread1_cdm);
SystemCoreClockUpdate();
osKernelInitialize (); // initialize CMSIS-RTOS
osThreadNew(thread1,NULL,NULL);
osThreadNew(thread2,NULL,&thread2_attr);
osKernelStart(); // start thread execution
while(1);
}
- Filename: serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "GPIO_STM32F10x.h"
#include "Driver_USART.h"
#include <cmsis_os.h>
extern ARM_DRIVER_USART Driver_USART1;//导入RTOS API的USART1
extern ARM_DRIVER_USART Driver_USART2;
#define Serial1 Driver_USART1 //转换为更加熟悉的Serial1
#define Serial2 Driver_USART2
char USART1_ReadByte(void);
char USART1_SendByte(char data);
char USART2_ReadByte(void);
char USART2_SendByte(char data);
uint8_t USART2_Read(char buffer[],uint8_t num);
uint8_t USART2_ReadLine(char buffer[]);
uint8_t USART1_RecNum(void);
uint8_t USART2_RecNum(void);
void USART1_callback(uint32_t event);
void USART2_callback(uint32_t event);
void Serial2_Conf(uint32_t baud);
void Serial1_Conf(uint32_t baud);
int Serial1_Print(char str[]);
int Serial2_Print(char str[]);
#endif /*__SERIAL_H*/
- Filename: serial.c
#include "serial.h"
char RxBuf1[256]={0}; //自定义的接收缓存
static uint8_t Num_U1RxByte = 0; //USART1 接收的数据字节数
static uint8_t U1RxF = 0; //索引,指向USART1 接收的数据字缓存中未读数据的第一个
static uint8_t U1RxL = 0;//索引,指向USART1 接收的数据字缓存中未读数据的最后一个
char Rx1; //一个临时接收变量
//这里将缓存用作FIFO,将索引定义为8位无符号数,范围正是缓存的大小,
//溢出后正好回到缓存的开始,遍历所有缓存
char RxBuf2[256]={0};
static uint8_t Num_U2RxByte = 0;
static uint8_t U2RxF = 0;
static uint8_t U2RxL = 0;
char Rx2;
/*
*@brief: 从USART1缓存中读走一个字节
*/
char USART1_ReadByte(void){
if(Num_U1RxByte == 0) return '\0';
Num_U1RxByte--;
return RxBuf1[U1RxF++];
}
/*
*@brief: USART1发送一个字节,直至发送完,超过10s算超时,返回1
*/
char USART1_SendByte(char data){
uint16_t i = 0;
Serial1.Send(&data,1);
for(;i<1000;i++){
if(Serial1.GetTxCount()>=1)return 0;
osDelay(10);
}
return 1;
}
char USART2_ReadByte(void){
if(Num_U2RxByte == 0) return '\0';
Num_U2RxByte--;
return RxBuf2[U2RxF++];
}
char USART2_SendByte(char data){
uint16_t i = 0;
Serial2.Send(&data,1);
for(;i<1000;i++){
if(Serial2.GetTxCount()>=1)return 0;
osDelay(10);
}
return 1;
}
/*
*@brief: 获取USART1缓存RxBuf1中缓存的字节数
*/
uint8_t USART1_RecNum(void){
return Num_U1RxByte;
}
uint8_t USART2_RecNum(void){
return Num_U2RxByte;
}
/*
*@brief: 配置USART1,且使波特率为baud
*/
void Serial1_Conf(uint32_t baud){
/*Initialize the USART driver */
Serial1.Initialize(USART1_callback);
/*Power up the USART peripheral */
Serial1.PowerControl(ARM_POWER_FULL);
/*Configure the USART to baud Bits/sec */
Serial1.Control(ARM_USART_MODE_ASYNCHRONOUS |
ARM_USART_DATA_BITS_8 |
ARM_USART_PARITY_NONE |
ARM_USART_STOP_BITS_1 |
ARM_USART_FLOW_CONTROL_NONE, baud);
/* Enable Receiver and Transmitter lines */
Serial1.Control (ARM_USART_CONTROL_TX, 1);
Serial1.Control (ARM_USART_CONTROL_RX, 1);
Serial1.Receive(&Rx1,1);
}
void Serial2_Conf(uint32_t baud){
/*Initialize the USART driver */
Serial2.Initialize(USART2_callback);
/*Power up the USART peripheral */
Serial2.PowerControl(ARM_POWER_FULL);
/*Configure the USART to baud Bits/sec */
Serial2.Control(ARM_USART_MODE_ASYNCHRONOUS |
ARM_USART_DATA_BITS_8 |
ARM_USART_PARITY_NONE |
ARM_USART_STOP_BITS_1 |
ARM_USART_FLOW_CONTROL_NONE, baud);
/* Enable Receiver and Transmitter lines */
Serial2.Control (ARM_USART_CONTROL_TX, 1);
Serial2.Control (ARM_USART_CONTROL_RX, 1);
Serial2.Receive(&Rx2,1);
}
/*
*@brief: USART1发送输出一个字符串,直至发送完毕
*/
int Serial1_Print(char str[]){
int length = 0;
uint16_t i = 0;
while(str[length++]);
length--;
Serial1.Send(str,length);
for(;i<1000;i++){
if(Serial1.GetTxCount()>=length)return length;
osDelay(30);
}
return -1;
}
int Serial2_Print(char str[]){
int length = 0;
uint16_t i = 0;
while(str[length++]);
length--;
Serial2.Send(str,length);
for(;i<1000;i++){
if(Serial2.GetTxCount()>=length)return length;
osDelay(30);
}
return -1;
}
/*
*@brief: USART2读取num个数据,保存的buffer中
*/
uint8_t USART2_Read(char buffer[],uint8_t num){
uint8_t count = 0;
for(;count<num;count++){
buffer[count] = USART2_ReadByte();
}
buffer[count] = '\0';
return count;
}
/*
*@brief: 从缓存中等待读取一整行数据
*@note: for line ,condition is "\r\n"
*/
uint8_t USART2_ReadLine(char buffer[]){
uint8_t p=0;
while(1){
if(USART2_RecNum()){
buffer[p] = USART2_ReadByte();
if(buffer[p] == '\n'){
buffer[--p] = 0;
break;
}
else{
p++;
}
}
}
return p;
}
/*
*@brief: RTOS API中USART1的事件回调函数
*@function:在这里仅将将数据转移到缓存中
*/
void USART1_callback(uint32_t event){
/*
uint32_t mask;
mask = ARM_USART_EVENT_RECEIVE_COMPLETE |
ARM_USART_EVENT_TRANSFER_COMPLETE |
ARM_USART_EVENT_SEND_COMPLETE |
ARM_USART_EVENT_TX_COMPLETE ;
if (event & mask) {
}
if (event & mask) {
// Success: Wakeup Thread
//osSignalSet(tid2, 0x01);
}
if (event & ARM_USART_EVENT_RX_TIMEOUT) {
// __breakpoint(0); // Error: Call debugger or replace with custom error handling
}
if (event & (ARM_USART_EVENT_RX_OVERFLOW | ARM_USART_EVENT_TX_UNDERFLOW)) {
// __breakpoint(0); // Error: Call debugger or replace with custom error handling
}
*/
if (event & ARM_USART_EVENT_RECEIVE_COMPLETE) {
RxBuf1[U1RxL++] = Rx1;
Serial1.Receive(&Rx1,1);
if(Num_U1RxByte < 255)Num_U1RxByte++;
else{
U1RxF++; //当buffer满了丢弃最先收到的1个数据
}
}
/*
if (event & ARM_USART_EVENT_TRANSFER_COMPLETE) {
U1Tx_Flag = true;
}*/
}
void USART2_callback(uint32_t event){
if (event & ARM_USART_EVENT_RECEIVE_COMPLETE) {
RxBuf2[U2RxL++] = Rx2;
Serial2.Receive(&Rx2,1);
if(Num_U2RxByte < 255)Num_U2RxByte++;
else{
U2RxF++;
}
}
/*
if (event & ARM_USART_EVENT_TRANSFER_COMPLETE) {
U2Tx_Flag = true;
}*/
}