基于STM32的物联网项目经验总结与技术分享(一:串口通信)

最近刚做完一个项目,基于STM32做的,使用到了物联网相关的技术,准备做个简单的经验分享和技术总结,即一些外设的使用方法和驱动代码,例如使用AT24C08作为EEPROM外部存储,使用IIC通信;使用ESP8266与阿里云服务器进行通信,用到的是Mqtt协议,(也有在考虑到安全性,换一个服务器,或者自己搭建一个,没这方面经验,有经验的各位也可以指点一下),以及串口通信涉及到的所有问题;定时器的使用;收发信息的几种方式,截取任意字符串的通用方法,以及一些项目中的经验总结等(目前只想到这么多,后续想到会添加),本文的意义呢,是想要总结一种基于嵌入式物联网开发的方法,用于任何物联网项目中同时从基础讲起,供初学者学习,若有不对,为笔者水平不够,烦请指正。

这篇博客算是起个提纲挈领的作用吧,不同于常规的教程,直接从串口通信开始讲起,有看懂的可以在评论区留言或者私信我。我会在空闲时一一解答


  • 先简单回答几个问题,为什么使用stm32呢

因为本次项目涉及到的所有电机,推杆,舵机等结构的控制都是可以单线程解决的,时间也比较紧,用32做是最快最省的,后续会进行产品升级,准备用实时操作系统freertos去做。

  • 项目中遇到的一些繁琐头疼的问题

上一篇博客提到的多串口通信同时收发信息的问题,绝对排的上号,其次是不同的单片机通信的时候一定要注意硬件上电平的问题,比如供电,共地等,最后就是不同的串口通信收发信息方式会大大影响到Wi-Fi连接手机和服务器的速度。

具体怎么解决这些问题会在接下来的几篇博客中挨个分享开源

这篇文章除了介绍下这个项目覆盖到的知识点和难点,先简单的向大家介绍一下项目中最基础的串口通信吧。

首先简单直白的讲一下串口是什么,你可以认为是一根数据线,能在不同设备之间传输信息,是单片机中固有的资源,可以对其进行初始化以后使用,不同单片机的串口资源是不同的。会进行区分讲解

第一个问题,怎么初始化串口

讲怎么初始化串口之前,先说下什么是初始化吧,即对即将使用到的资源的各种属性按照你的需求,在其可供选择的范围之内进行设置,使得每次运行程序使用该资源的时候,他都可以按照你的设置去工作。

现在讲讲怎么初始化,串口,使用它进行信息收发,那势必会使用到两个引脚,Tx和Rx,那么我们得先学会引脚的初始化,即GPIO的初始化。GPIO有什么属性我们一一来看,首先是他的速率,其次是他的8种模式,以及管脚号,这是他的属性,初始化不止这些,在设置属性之前先要给他使能,那么知道它在哪个总线上是必须的(后续有详细介绍),可以通过编辑器查看,也可以记下来,使能完成以后,就用一个结构体完成对他的初始化吧,代码如下:

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能时钟
  
  GPIO_InitTypeDef GPIO_InitStruct;//用结构体初始化其属性
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5; //管脚号
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;//模式,其余几种稍后详解
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//速率
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//一般用不到
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;//一般用不到
  GPIO_Init(GPIOA, &GPIO_InitStruct);//到这里就完成了

这是刚提到的八种GPIO_MODE,简单介绍一下

STM32的GPIO模式有以下8种:
typedef enum
{ GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;(这是keil里的结构体,下面是我带有解释性的模式)
1. 输入模式(Input mode)
当GPIO配置为输入模式时,它将读取外部信号,并将其传输到CPU或其他系统级组件。该模式具有低功耗和高稳定性的特点。
2. 输出模式(Output mode)
当GPIO配置为输出模式时,它将向外部设备或其他系统级组件发送信号。这个模式可以用于驱动LED,马达,蜂鸣器等外部设备。该模式具有较高的稳定性,低功耗和高灵活性。
3. 复用模式(Alternate mode)
当GPIO配置为复用模式时,它将多个外设连接到同一个GPIO引脚上。每个外设都对应特定的GPIO模式。例如,UART通信使用不同的GPIO模式进行收发信号的复用。该模式具有较高的可扩展性和灵活性。
4. 模拟模式(Analog mode)
当GPIO配置为模拟模式时,它将成为模拟输入或输出引脚,用于输入或输出模拟电信号。该模式适用于模拟信号需要传输的场合,具有较高的精确度和可靠性。
5. 推挽输出模式(Push-Pull output mode)
当GPIO配置为推挽输出模式时,它将可以提供较高电平或较低电平输出,但不会产生中间状态。该模式适用于双向电路的输出控制,具有较高的灵活性,但功耗相对较高。
6. 开漏输出模式(Open-Drain output mode)
当GPIO配置为开漏输出模式时,它将可以提供较低电平,但不能提供较高电平输出。该模式用于驱动一些带有外部上拉电路的设备,或用于实现多设备输出控制,具有较高的灵活性和节能效果。
7. 浮空输入模式(Floating input mode)
当GPIO配置为浮空输入模式时,它将读取周围环境的电信号,但不会被任何外部电路影响。该模式适用于单次触发事件的输入读取,具有较高的准确度和稳定性。
8. 串行外设模式(Serial peripheral interface mode,SPI)
当GPIO配置为SPI模式时,它将与SPI总线进行通信,用于接口芯片、LCD等外设。该模式具有高速数据传输和低功耗的特点,适合高速外设通信。

讲完初始化引脚以后,我们可以系统的认识一下串口通信了:

STM32的串口通信主要涉及到以下知识点:

1. 串口通信协议:串口通信是一种异步通信方式,数据按照固定的帧格式传输。常见的帧格式包括帧起始位、数据位、停止位和校验位。(这些都可以通过配置串口初始化结构体来设置,后续会在程序中讲解)

2. STM32有哪些串口通信模块:STM32的串口通信模块包括USART、UART、LIN等。其中,USART具有较强的功能和扩展性,支持同步和异步通信。本篇文章主要想讲的也是USART串口通信

3. STM32的串口通信硬件连接:串口通信需要将Tx和Rx引脚连接到对应设备的Tx和Rx引脚上,同时需要连接共地(这是很重要的事,否则可能会出现收发的信息乱码,导致无法通信)。

接下来,以USART1为例,介绍STM32的串口通信代码实现方式。

1. 首先,需要打开USART1的时钟。这可以通过设置相应的时钟寄存器来实现。例如,要使用USART1,需要打开USART1的时钟,可以使用以下代码:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

怎么判断串口位于哪个总线上,很简单,在你的keil5中点击这个函数,跳转函数定义,就可以看到具体哪个设备在哪个总线上了,我简单总结一下:

总线一(编辑器就是这么写的,我原封不动的放这里,给大家熟悉熟悉)

  * @brief  Enables or disables the Low Speed APB (APB1) peripheral clock.
  * @param  RCC_APB1Periph: specifies the APB1 peripheral to gates its clock.
  *   This parameter can be any combination of the following values:
  *  @arg RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4,
  *          RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7,
  *          RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3,
  *          RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_USART4, 
  *          RCC_APB1Periph_USART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2,
  *          RCC_APB1Periph_USB, RCC_APB1Periph_CAN1, RCC_APB1Periph_BKP,
  *          RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC,
  *          RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None

总线二
  * @brief  Enables or disables the High Speed APB (APB2) peripheral clock.
  * @param  RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
  *   This parameter can be any combination of the following values:
  *  @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11     
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None

特殊的总线,基本用不到

  * @brief  Forces or releases AHB peripheral reset.
  * @note   This function applies only to STM32 Connectivity line devices.
  * @param  RCC_AHBPeriph: specifies the AHB peripheral to reset.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_AHBPeriph_OTG_FS 
  *     @arg RCC_AHBPeriph_ETH_MAC
  * @param  NewState: new state of the specified peripheral reset.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None

2. 设置USART1的参数。参数设置可以使用USART_InitTypeDef结构体进行设置。例如,可以设置波特率为9600,数据位为8位,停止位为1位,校验位为无校验(基本上是大家默认的配置,其实还有别的配置属性,在这里就一笔带过吧,有8位和9位数据长度,奇偶校验无校验,0.5/1/1.5/2长度的停止位),可以使用以下代码:

USART_InitStructure.USART_BaudRate = bound;                           //波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;       //8个数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1;            //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_ITConfig函数打开USART1的中断,在需要用到中断的时候,还需要配置一些别的参数。

说一下抢占优先级和响应优先级,具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套在低抢占式优先级的中断中。当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。总结下便是:抢占式优先级>响应优先级>中断表中的排位顺序

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //设置中断向量分组:第2组 抢先优先级:0 1 2 3 子优先级:0 1 2 3	
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);          //开启接收中断    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;       //设置串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//响应优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//中断通道使能
NVIC_Init(&NVIC_InitStructure);	                        //设置串口1中断

 同时还需要编写对应的USART1_IRQHandler(注意命名不要错了,是唯一的函数名)函数处理中断函数,下面是一个示例:

void USART1_IRQHandler(void)

{

  if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)

  {
//先简单带过,不想篇幅过长,下篇再讲
  }

}

完整的串口初始化程序为:

void uart1_init(u32 bt)    
{    
    GPIO_InitTypeDef GPIO_InitStructure;    //声明一个结构体变量,用来初始化GPIO    
    NVIC_InitTypeDef NVIC_InitStructure;     //中断结构体定义    
    USART_InitTypeDef  USART_InitStructure;   //串口结构体定义    

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO,ENABLE);    

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX    
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;    
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;    
    GPIO_Init(GPIOA,&GPIO_InitStructure);    
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX    
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;    
    GPIO_Init(GPIOA,&GPIO_InitStructure);    


    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);     
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;     
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;     
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;     
    NVIC_Init(&NVIC_InitStructure);    


    USART_InitStructure.USART_BaudRate=bt;   //波特率设置为bt    
    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_Rx|USART_Mode_Tx;    
    USART_Init(USART1,&USART_InitStructure);    
    USART_Cmd(USART1, ENABLE);    
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//使能或者失能指定的USART中断 接收中断    
    USART_ClearFlag(USART1,USART_FLAG_TC);//清除USARTx的待处理标志位  

}  

或许里面还有一些大家看不懂的,但是全写篇幅太长了, 串口的使用先讲到这里,下一部分讲解串口的使用以及收发信息的方法,以及串口相关的项目经验。

行文至此,第一篇教程类文章结束,读者可以针对此文提一些建议,下篇文章将会采纳。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super落尘君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值