stm32串口驱动和esp8266的使用

2023-11-11

写在前面

本文并不对相关知识进行讲解,只是这次的实验课要实现的任务有些复杂,我也踩了一些坑,对代码实现思路进行复现和记录,并不是技术科普性文章,基础知识还是要自己有所掌握。


1.stm32的串口通讯
  • 开发板:stm32f407zgt6课程学习板
  • 下载器:j-link

串口通信是单片机一种基础的通信协议,对时序要求比较严格,一般都是通过硬件实现。stm32初始化串口通讯:

首先查看原理图type-b接口对应的单片机引脚(这一步老师初始化里已经配置好:
type-b的接口连线
在这里插入图片描述

跳帽连线,可以看到需要将uart3 tx/rx的引脚通过跳帽选择连接到单片机
在这里插入图片描述

对应单片机的引脚:为tx-pc10 rx-pc11
在这里插入图片描述

之后我们就可以通过库函数对应配置

void UART3_Configuration(void)
{
    GPIO_InitTypeDef    GPIO_InitStructure;
    USART_InitTypeDef   USART_InitStructure;
    //  开启GPIO_D的时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    //  开启串口3的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//复用推挽输出
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
	//串口对应设置
    USART_InitStructure.USART_BaudRate   = 115200;//波特率
    //波特率是一个知识点,可能之后考试会考(确信)
    //不使用校验位,所以是八位
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits   = USART_StopBits_1;//停止位
    //不使用校验位
    USART_InitStructure.USART_Parity     = USART_Parity_No;
    //流控信号选择
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	//发送和接收同时使用过
    USART_InitStructure.USART_Mode                = USART_Mode_Tx | USART_Mode_Rx;
    USART_Init(USART3, &USART_InitStructure);
        //接收中断使能,可以跳转到函数定义的头文件看第二个参数的作用
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
    /* Enable the USARTx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
	/* 使能串口3 */
    USART_Cmd(USART3, ENABLE);
}

配置好串口,相当于我们把消息通讯的机器组装好,接下来就要进行发送和接收消息的配置。

发送函数:

void USART3_Senddata(unsigned char *Data, unsigned int length)
{
    while(length--)
    {
        USART_SendData(USART3,*Data++);
        while (USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET);
    }
}

这里USART_SendData是标准库提供的函数,发送一个字节,硬件在发送数据时,发送状态会进行标志位置位,我们只需要进行判断读取是否发送完成再进行下一个字节的发送就好了,所以这里用while()循环一下。

printf的重定向:这里有一个小应用是关于c语言中printf函数的使用,看到这里想必大家都会明白,数据传输的底层就是对每一个字节的传输,如果我们只是对字符char的传输,那么一个字节就可以实现很好通讯,但是如果我们想对整数和浮点数进行信息传递,那么势必要从他们的存储方式进行转换,这是很麻烦的一件事。那么我们就会想到c语言里好像无所不能打印的printf,如果我们能直接使用他不就能解决大部分事情了。所以很多单片机的示例代码里都会对printf进行重定向配合串口实现和上位机的通讯,以便于方便的对单片机进行debug。而printf的底层就是调用fputc对文件写下一个字符。因此我们可以看到以下代码:

int fputc(int ch, FILE *f)
{
    USART3->SR;              // 防止复位后无法打印首字符
    USART_SendData(USART3, (u8) ch);
    while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);

    return (ch);
}

这样我们就可以使用printf和电脑进行通讯了。
进行初步尝试,查看是否配置正确。

int main()
{
    SysTick_Init();
    UART3_Configuration();
	printf("hello world\n");
	while(1){
	
	}
}

下载程序,把使用type-b线连接电脑和板子,打开上位机连接显示ch340对应的com口。按下复位键,看到上位机接收到hello world,表示成功。(记得将跳帽插到232的那一侧)。
在这里插入图片描述
在这里插入图片描述
以上是发送
同时还有接收部分

可以在接收startup_stm32f4xx.s文件中找到USART3_IRQHandler中断函数

void USART3_IRQHandler(void)
{
    if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)                //  判断中断标志位
    {
        USART_ClearITPendingBit(USART3, USART_IT_RXNE);
        USART_ReceiveData(USART3);  //把发来的消息接收一下
        printf("123");
    }
}

再次连接单片机和电脑,使用上位机向单片机发送消息,得到以下现象:
在这里插入图片描述

说明接收中断配置成功。

2.esp8266

在配置好串口之后,对于与单片机进行串口通讯的其他外设,就只需要进行发送和接收消息的处理了。

初始化

看看wifi模块对应的引脚
在这里插入图片描述

可以看到这里使用的是uart5串口,对应的单片机引脚是rx-pd2,tx-pc12。我们只需要对上面的usart3对应的初始化函数进行修改,就可以配置成功,这里不在赘述,值得一提的是,uart5资源在stm32f4中只有异步串口功能,因此在英文组成上是uart而非usart。
uart5的配置:

void UART5_Configuration(unsigned int baud)
{
    GPIO_InitTypeDef    GPIO_InitStructure;
    USART_InitTypeDef   USART_InitStructure;
    NVIC_InitTypeDef    NVIC_InitStructure;
    Uart5.ReceiveFinish = 0;
    Uart5.RXlenth = 0;
    Uart5.Time = 0;
    Uart5.Rxbuf = Uart5ReceiveBuf;
    //  开启GPIOA的时钟  
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOC, ENABLE);
    //  开启串口1的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);
  
    USART_InitStructure.USART_BaudRate   = baud;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits   = USART_StopBits_1;
    USART_InitStructure.USART_Parity     = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode       = USART_Mode_Tx | USART_Mode_Rx;
    USART_Init(UART5, &USART_InitStructure);
    /* 使能串口1 */   /* Enable the USARTx Interrupt */
    USART_Cmd(UART5, ENABLE);
    USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);
    
    /* NVIC configuration */
    NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    USART_Cmd(UART5, ENABLE);
}

void UART5_Senddata(unsigned char *Data, unsigned int length)
{
    while(length--)
    {
        USART_SendData(UART5,*Data++);
        while (USART_GetFlagStatus(UART5, USART_FLAG_TC)==RESET);
    }
}
与esp8288进行通讯

大家在做这块实验的时候,最多的是不知道模块是否正确的工作了,只看手机有没有搜到热点,如果没有又不知道哪里出现了问题,手足无措。我们可以在网上搜以下这个模块的使用方法,看一些特性,可以让我们更好的进行调试。
对于esp8266,它具有回显功能,即他接收到消息就会将消息原封不动的返回,并且返回对应的应答。利用这个我们可以看到我们的消息发送和接收是否正常。因此我们首先要处理从模块中接收的应答消息,并且如果能把他想办法打印出来,不就可以看到哪里出现了问题。

首先定义一个接收消息的结构体。然后在接收中断里使用。

struct UsartData                                          
{      
    unsigned char *Rxbuf;
    unsigned int   RXlenth;
    unsigned char  Time;
    unsigned char  ReceiveFinish;
};
USARTDATA   Uart5;
void UART5_IRQHandler(void)
{
    if (USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)         
    {
        USART_ClearITPendingBit(UART5, USART_IT_RXNE);
        Uart5.Rxbuf[Uart5.RXlenth++] = USART_ReceiveData(UART5);
        Uart5.Time = 5; //表示正在接收
    }
}

然后在系统滴答定时器里发送这个接受的数据到uart3

void SysTick_Handler(void)
{    
    if (Uart5.Time > 0)
    {
        Uart5.Time--;
        if (Uart5.Time == 0)
        {
            Uart5.ReceiveFinish = 1;
            USART3_Senddata(Uart5.Rxbuf, Uart5.RXlenth);
            Uart5.RXlenth = 0;
        }
    }
}

然后我们就可以在主函数里对模块发消息进行控制。

在烧录初始化模块的代码之后要重新上电,否则模块不会发送消息,很奇怪
先发个“AT”看看是否有响应

int main(void)
{
    unsigned char test[11] = "WIFI test\r\n";
    SysTick_Init();             //  系统滴答定时器初始化
    // LEDGpio_Init();
    // KEYGpio_Init();
    UART3_Configuration();                                              //  USART3配置
    UART5_Configuration(115200);
    printf("hello world\n");
  
    UART5_Senddata("AT\r\n",4);
    delay_ms(5);
    //WIFI_Configuration();  
    while (1)
    {

    }
}
发送的消息不完整

发现他有反应,虽然但是很奇怪,我们明明发的“AT”,他却只给我们回了一个“T”,这个不知道问题出在哪里,我试了很多方法,但是每次初始化完成之后的第一条语句之后都是这样,会丢到一个字母。

    UART5_Senddata("AT\r\n",4);
    delay_ms(5);
    UART5_Senddata("AT\r\n",4);
    delay_ms(5);

所以我们把这句话发两次,再看
在这里插入图片描述

发现第二句话反应就正常了

配置wifi名和密码消息接收不全

然后我们再配置wifi名和密码

unsigned char CWSAP[38]     = "AT+CWSAP=\"Ctrl\",\"0123456789\",5,3\r\n";
UART5_Senddata(CWSAP,sizeof(CWSAP));
delay_ms(5);

在这里插入图片描述

这里他虽然接收到了消息并且回了ok,但是他回显有缺失,虽然不太明白为什么,但是只要把这句话的发送位置放在配置消息第一个,回显就会正常,而且正常配置密码的命令是后面跟的命令是5,3 改掉之后重新上电就会看到有密码的wifi了。

回显busy的问题

大家也看到了我在每次发送消息之间都会延时5ms,这是因为虽然你发送指令是按照一句一句发的,但是他本质还是在连续的发送char,每个语句之后模块会进行响应的设置,无法快速的接收下一条指令。

其实到这里我们的模块就已经调通了,之后只需要根据任务手册做下去实验就已经能够完成。


还有一些值得说的东西。

esp8266是现在很火的带wifi的mcu,性价比很高成本也很低,感兴趣的话可以学习一下他的at指令。

打算从他开始,入手以下esp32家族,最近势头很猛的国产32位单片机,在生态活跃上和物联网方面都势头很猛。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32串口驱动和esp8266的使用 的相关文章

随机推荐

  • 轴承公差以及常见的轴孔公差配合

    1 轴承公差配合原则 动圈过盈 静圈间隙 2 轴承内圈要拆卸 过渡配合 安装轴承外圈孔的公差H7 G7 K7 3 配套轴承内圈的 轴0 0 005 4 齿轮轴孔配合 齿轮孔H7 齿轮轴不拆的话 0 005 0 008 齿轮轴轻松拆除的话f6
  • tq210-uboot eth dm9000移植

    这个芯片与ok210一样 因此将代码搬过来就ok了 但是使用tftp 启动 TQ210自带的kernel依旧是 Start kerneling 就啥都没有了 启动设置如下 set machid 998 set serverip 192 16
  • MIPI CSI协议

    PCLK 像素时钟 每个时钟对应了一个像素数据 HSYNC 行同步信号 VSYNC 帧同步信号 像素字节转换层 sensor输出的4种数据类型 YUV422 RGB RAW JPEG RGB Data type description 0x
  • 《面试准备》c/c++最少装箱问题(动态规划)

    问题描述 出口质量不等的钻石n颗 至少需要多少个箱子 输入 一个整数m 箱子最大承载重量 一个整数n 钻石的个数 第i颗钻石的质量大小a i 输出 最少需要多少箱子 举例 输入 10 5 4 5 7 3 6 输出 3 c 代码实现 动态规划
  • java8 HashMap

    java8 HashMap 8以前HashMap是用位桶 链表的形式 8以后HashMap是用位桶 链表 红黑树的形式 冲突节点数大于8时 转换成红黑树
  • Spark 架构,计算

    1 架构设计图 2 用户交互方式 1 spark shell spark命令行方式来操作spark作业 多用于简单的学习 测试 简易作业操作 2 spark submit 通过程序脚本 提交相关的代码 依赖等来操作spark作业 最多见的提
  • requests 登陆的几种方法

    一 通过账户名和密码登陆访问 formData username password 需要带 cookies 则带上 cookies res req post url data formData cookies cookies headers
  • sqlite,mysql,access对比

    SQLite是一个小型的桌面型数据库 轻量级的 绿色 开源 轻便 SQLite其实只是一个文件 以及内部格式方案而已 下面做几个简单的对比 SQLite VS 文本文件或二进制文件 他们的本质是相同的 都是一个文件 但是SQLite定义了更
  • Matlab:大小写和空格敏感性

    Matlab 大小写和空格敏感性 Matlab是一种强大的计算机语言 是许多科学家 工程师和其他专业人士使用的首选语言之一 当你开始学习Matlab时 可能会发现它与其他编程语言有些不同 其中一个最显著的区别就是Matlab对大小写和空格的
  • VScode无法启动问题解决思路

    VScode无法启动问题解决 过程 后记 过程 在不知道为什么的情况下 VScode启动没有反应 然后尝试解决问题 进行以下尝试 重启 重装 卸载注册表重装 删除配置重装 均不行 然后本来想打算重装系统了 最后还是接着搞一搞 然后就打算用P
  • MySQL字符集设置

    ERROR 1366 HY000 Incorrect string value错误解决办法 通过命令查看Mysql默认字符集的相关设置 mysql gt SHOW VARIABLES LIKE character Variable name
  • 9.Java面向对象基础(上)

    个人简介 作者简介 大家好 我是W chuanqi 一个编程爱好者 个人主页 W chaunqi 支持我 点赞 收藏 留言 愿你我共勉 没有什么比勇气更温文尔雅 没有什么比怯懦更冷酷无情 文章目录 面向对象 上 1 面向对象的思想 1 1
  • 前端开发工程师

    岗位职责 1 负责网站前端页面的开发工作 2 根据产品需求 分析并给出最优的页面前端结构解决方案 3 与设计师合作完成网站页面前端的特效和最新的应用 4 与产品经理合作完成前端网站交互效果的实现 职位要求 1 掌握各种修图软件 如PS Fi
  • 数据库系统工程师真题及详解(2015~2021)

    2015 2021年软考中级数据库系统工程师真题及答案详解 链接 https pan baidu com s 1VMXyrl1cBX Gwoz0EU Dow pwd nt0a 提取码 nt0a 据了解 考试试题大部分与历年真题相近 祝大家软
  • 计组

    目录 一 知识点 1 寻址方式什么 2 根据操作数所在的位置 都有哪些寻址方式 3 直接寻址 4 立即寻址 5 隐含寻址 6 相对寻址 7 寄存器 8 寄存器 寄存器型 RR 寄存器 存储器型 RS 和存储器 存储器型 SS 9 基址寻址方
  • 类成员函数的重载、覆盖和隐藏区别

    转自 http blog csdn net yanjun 1982 archive 2005 09 02 470405 aspx 这三个概念都是与OO中的多态有关系的 如果单是区别重载与覆盖这两个概念是比较容易的 但是隐藏这一概念却使问题变
  • 理清概念:同步与异步

    广义的同步与异步 在广义上 同步和异步是描述两个或多个事件 操作或进程之间的关系 同步意味着事件 操作或进程是有序的 一个操作必须在另一个操作完成后开始执行 异步则意味着事件 操作或进程是独立的 可以在不等待其他操作完成的情况下开始执行 同
  • CentOS7 RPM包管理功能总结及示例

    RPM是红帽软件包管理器 主要用来对RPM包进行安装 升级 卸载 查询 校验和数据库维护的管理操作 安装 语法 rpm i install install options PACKAGE FILE i 安装一个新包 PACKAGE FILE
  • 关于C#中get和set

    转自 http blog sina com cn s blog 82526aa60100txtx html 在程序中经常碰到get set 不甚明白 在网上查询时也说的迷迷糊糊 所以整理下 以学的明白透彻点 有两个类person publi
  • stm32串口驱动和esp8266的使用

    写在前面 本文并不对相关知识进行讲解 只是这次的实验课要实现的任务有些复杂 我也踩了一些坑 对代码实现思路进行复现和记录 并不是技术科普性文章 基础知识还是要自己有所掌握 1 stm32的串口通讯 开发板 stm32f407zgt6课程学习