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

文章讲述了在STM32微控制器中处理串口通信的方法,包括串口中断服务函数的使用,以及如何通过定义串口指针和重定向printf函数来实现多串口管理。此外,还提到了DMA收发作为另一种数据传输方式,可以在不占用CPU的情况下进行数据搬运。
摘要由CSDN通过智能技术生成

本篇文章进行串口发信息的讲解,我们接上文来看

上一章介绍了串口通信,讲到了中断服务函数部分。简单说下什么是串口中断服函数,串口中断服务函数是用于处理串口接收和发送数据的相关操作。在STM32中,串口中断服务函数的名称为USARTX_IRQHandler

void USART1_IRQHandler(void)  
{  
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  
  {  
    // 处理接收数据  
    while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET)  
    {  
      // 处理接收缓冲区的数据  
    }  
  }  
  
  if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)  
  {  
    // 处理发送数据  
    USART_SendData(USART1, 0x0);  
  }  
}

做一个解释:在上面的代码中,我们首先通过USART_GetITStatus()函数检查是否接收到新的数据,如果接收到新的数据,则通过USART_GetFlagStatus()函数检查接收缓冲区中是否有数据待处理。如果有数据待处理,则可以对接收缓冲区中的数据进行处理。如果发送缓冲区中有数据待发送,则可以通过USART_SendData()函数发送数据。(我一般使用printf函数去发送信息),在C语言库函数里,已经有了封装好的printf 函数,print函数的在多串口使用的注意点在上一篇文章中进行了介绍,这里再次进行一个总结,

先描述一个多串口环境,一般在进行物联网开发的时候,都是多串口环境:串口1用作调试串口,串口2用作MQTT通信串口,串口3用作下位机通信串口

若是三个串口都使用printf函数,会出现什么情况呢?如果有尝试过的读者,可以知道,这种情况下,串口就不能正常工作了,那么该怎么合理的去使用printf资源呢?我这边总结了一种解决STM32多串口同时收发的方法:即使用串口指针和printf函数重定向来解决

我来解释一下思路:

先定义串口指针(初学者会用就行,暂时不必深究)

其次,进行重定向:重定向c库函数printf到串口

然后对使用到的串口进行初始化,串口初始化在上一章已经讲过,此处一笔带过

再根据所需进行串口中断服务函数的编写

代码分块讲解:

第一部分:定义串口指针

USART_TypeDef * DEBUG_USARTx = USART1;   //定义串口指针,
//通过指针指到使用串口的各个寄存器,(SR状态寄存器,DR数据寄存器)

第二部分:重定向

//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

 这里读者可能不太理解,什么是重定向:

printf重定向是指将printf函数的输出从默认输出改为其他设备,如串口或LCD显示屏等。实现printf重定向可以通过重定义printf函数调用的方式来实现。例如,在使用串口时,可以将USART_SendData函数替换为fputc函数,从而使printf函数的输出发送到串口。需要注意的是:要使用printf重定向功能,需要导入stdio.h头文件。

第三部分:串口初始化,此处以串口一三为例:

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的待处理标志位  

}    
void uart3_init(u32 bt)    
{    
    USART_InitTypeDef USART_InitStructure;    
    NVIC_InitTypeDef NVIC_InitStructure;     
    GPIO_InitTypeDef GPIO_InitStructure;    //声明一个结构体变量,用来初始化GPIO    
   //使能串口的RCC时钟    
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能UART3所在GPIOB的时钟    
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);    
 	USART_DeInit(USART3);  //复位串口3

   //串口使用的GPIO口配置    
   // Configure USART3 Rx (PB.11) as input floating      
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;    
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;    
   GPIO_Init(GPIOB, &GPIO_InitStructure);    
  
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;    
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    
   GPIO_Init(GPIOB, &GPIO_InitStructure);    

   //配置串口    
   USART_InitStructure.USART_BaudRate = 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(USART3, &USART_InitStructure);//配置串口3         
   USART_Cmd(USART3, ENABLE);//使能串口3    

   //串口中断配置          
   USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断 
   NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; 
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3   
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;    
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    
   NVIC_Init(&NVIC_InitStructure);    
}    

 上述代码中,我们只需要按照流程来配置参数即可。主要就是串口对应GPIO配置,串口基础配置,串口中断配置三部分。

定义完成以后,就可以按需添加了,在使用的时候,只需要按照以下格式即可:

//串口1:

DEBUG_USARTx = USART1;
printf(); //自定义即可


//串口2:

DEBUG_USARTx = USART2;

printf(); //自定义即可

//串口3:

DEBUG_USARTx = USART3;
printf(); //自定义即可

以上便是串口发信息的方法,下面再介绍一种办法:

除了printf之外还有什么办法发信息,答案之一是DMA收发,什么是DMA收发呢,专业的说法是:DMA收发是指直接存储器访问(Direct Memory Access,DMA)。是CPU 用于数据从一个地址空间到另一地址空间“搬运”的组件,数据拷贝过程不需CPU干预,数据拷贝结束则通知CPU处理。在大量数据拷贝时,使用DMA可以释放CPU资源。DMA数据拷贝过程包括:内存—>内存,内存间拷贝,外设—>内存,如uart、spi、i2c等总线接收数据过程,内存—>外设,如uart、spi、i2c等总线发送数据过程。简单些就是不占用CPU进行数据搬运的过程,可以把数据从一个地方搬运到另一个地方。

使用DMA收发也需要去编写DMA驱动函数,在下一篇进行总结。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Super落尘君

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

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

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

打赏作者

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

抵扣说明:

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

余额充值