基于STM32F103RCT6之手把手教你写智能家居项目(3)

本文介绍了串口模块的编写,复习了C语言中的strcmp、strcpy、strstr和strtok函数,并详细展示了USART1和USART2的配置以及中断处理函数,包括USART2_IRQHandler的实现。
摘要由CSDN通过智能技术生成

一:回顾

        上一节我们描述了云平台的使用方法,简述了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语言基础函数的复习。

        下节更精彩......

  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值