一:回顾
上一节我们描述了云平台的使用方法,简述了WIFI模块的基础知识,还有延时函数,WIFI模块代码的编写。
二:串口模块的编写
C语言基础知识复习
在串口模块的编写中,我们的串口配置可能不太会用到C的一些基础函数,但是在中断处理函数中,你要想实现对收发数据格式的判断,就会用到C中的strcmp,strstr等函数。所以我们在书写代码之前要对可能用到的C函数进行复习。
1.strcmp函数
#include <string.h>
int strcmp(const char *s1, const char *s2);
//比较 s1 和 s2 (从左往右依次比较对应位置上的字符,如果不相同,则停止比较)
//返回值:
//小于 等于 大于0 的数
//例子:
//strcmp("abcdefg","abcdefg"); // 返回0
//strcmp("abczefg","abcdefg"); // 返回大于0的数
//strcmp("abcdefg","abcxefg"); // 返回小于0的数
2.strcpy函数
#include <string.h>
char *strcpy(char *dest, const char *src);
//将src(包含'\0')拷贝到dest(从下标为0的位置开始存放)中
//dest必须足够大可以容纳拷贝的内容
//参数
//src : 源字符串
//dest : 目标
//返回值:
//返回dest
3.strstr函数
#include <string.h>
char *strstr(const char *haystack, const char *needle);
//该函数返回在haystack中第一次出现needle字符串的位置
//如果未找到则返回NULL
4.strtok函数
#include <string.h>
char *strtok(char *str, const char *delim);
//参数
//str为要被分解成为一组小字符串的字符串
//delim为包含分隔符的C字符串
//如果在使用过一次strtok后再在后面使用,且delim一样
//则将str替换成NULL可实现连续分割
好的,复习到了这里就算是结束了,那么下面我们开始写串口的配置函数和中断处理函数,我会在使用到上面函数的时候稍作提醒哦。
串口1的基本配置
我们在之前的WIFI烧录固件中了解到了,我们的WIFI模块是连接在串口2上面的,那么我们为什么要初始化串口1呢?是因为我们要通过串口1来发送数据,而且没用什么地方要用到串口1的中断,所以这里的串口1我们就不配置中断。
串口1配置代码
void usart1_Init(void)
{
//定义GPIO,USART结构体
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//USART1,GPIOA,AFIO时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
//GPIO引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口初始化配置
//波特率
USART_InitStructure.USART_BaudRate = 115200;
//无流控
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
//收发模式
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
//无奇偶校验位
USART_InitStructure.USART_Parity = USART_Parity_No;
//一位停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
//八位数据位
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStructure);
}
串口2的基本配置
在上一节的WIFI初始化代码中可以看到,发送AT指令的是串口2,所以必须配置串口2,至于中断为什么要配置,是因为接收中断可以让我们在用手机发送命令给设备的时候,可以快速的响应。
串口2配置代码
void usart2_Init(void)
{
//定义USART,GPIO,NVIC结构体
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//USART2,GPIOA,AFIO时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
//GPIO引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口初始化配置
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART2, &USART_InitStructure);
//NVIC中断配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//串口2通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
//开启串口接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //接收中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); //空闲中断
//清除发送完成标志位和发送数据寄存器空标志位
USART_ClearFlag(USART2, USART_FLAG_TC|USART_FLAG_TXE);
//串口使能
USART_Cmd(USART2, ENABLE);
}
main函数中要定义的全局变量
在写中断处理函数之前,我们要先定义一些全局变量,然后在usart.h中声明,以便后续的操作。
//创建一个buf用于保存接收的数据
char us2_buf[200];
//下标,用于接收每一位数据
uint8_t us2_index = 0;
//创建一个用于判断数据格式的标志
uint8_t us2_free = 0;
//创建一个data用于保存我要发送的指令
char us2_data[200];
//创建一个用于进入解包函数的标志
uint8_t us2_flag = 0;
串口2中断处理函数代码
//函数名必须要这样写,且必须声明,不然会使用原本的串口2中断处理函数
void USART2_IRQHandler(void)
{
//接收中断
if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
//清除标志位
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
//用USART1传输数据
USART_SendData(USART1, USART_ReceiveData(USART2));
//接收串口2数据
us2_buf[us2_index++] = USART_ReceiveData(USART2);
//判断发送数据的类型和格式
//完整的一帧数据应该是+IPD,6:ORDER:LED0+OPEN\r\n
//或者是+IPD,6:DATA1:LED0+OPENED\r\n
if(us2_buf[0]!='+'){
usart2_clean();
}else{
//判断数据是否符合格式
if(us2_index>3){
//回忆一下,这里应该用到什么函数
//有比较作用的
if(strcmp("+IPD",(const char*)us2_buf)){
us2_free = 1;
}
}
}
}
//空闲中断
//其实发送的两个字符之间间隔非常短,所以在两个字符之间不叫空闲。
//空闲的定义是总线上在一个字节的时间内没有再接收到数据。
//空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有
//再接收到数据的时候发生的。
//总线一般会在就只有一个数据帧发送完成的情况才会接收不到数据
//也称为帧中断
//举例:一次性发送10个字节,进入几次空闲中断?
//只会进入一次空闲中断
if(USART_GetITStatus(USART2,USART_IT_IDLE) != RESET)
{
//清除标志位
USART_ClearITPendingBit(USART2, USART_IT_IDLE);
//接收串口2数据
USART_ReceiveData(USART2);
//做消除/r/n操作,以便下面的解包函数对数据进行判断
if(us2_free == 1){
if((us2_buf[us2_index-1] == '\n') && (us2_buf[us2_index-2] =='\r')&&(us2_buf[us2_index-3]!='G')){
//我们消除了\r\n应该放在我们定义的us2_data里面,那么怎么放?
//有拷贝作用的函数是什么?
strcpy(us2_data,us2_buf);
us2_free = 0;
us2_flag = 1;
}
}
//buf清零
usart2_clean();
}
}
buf,index清零函数代码
void usart2_clean(void)
{
//在上面我还忘了说了,清零要用到memset函数
//void *memset(void *s, int c, size_t n);
//s的前n个字节用c来填充
memset(us2_buf,0,sizeof(us2_buf));
us2_index = 0;
}
三:总结
本节篇幅过长,看起来费劲,所以解包函数就放到下一节去写。
本篇主要讲述了串口模块代码的编写,以及一些C语言基础函数的复习。
下节更精彩......