跳转至

05.串口通信

实验说明

本系列实验都是基于STM32F1的单片机进行的编程实验。

实验简介

这里使用单片机的串口1来实现单片机的串口。

串口简介

串口是一种全双工的通信方式,意思就是可以在发的时候也接收,两台设备连接,己方设备TX连接对方设备RX,己方设备RX连接对方设备TX,然后只要配置的参数一样就可以通信了,常规的通信是1个字节8位的传输。串口发送数据是低位先行。

单片机一般会有几个串口,比如,F103ZET6就有5个串口。

代码实现

基础实现

先是初始化配置,开时钟->初始化引脚->初始化串口->初始化中断

void bsp_usart1_init(uint32_t bound)
{
    //打开时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    //引脚初始化
    {
        GPIO_InitTypeDef GPIO_InitStructure;

        //USART1_TX   GPIOA.9
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    
        GPIO_Init(GPIOA, &GPIO_InitStructure);

        //USART1_RX      GPIOA.10初始化
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        //GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
    }
    //串口初始化
    {
        USART_InitTypeDef USART_InitStructure;

        USART_InitStructure.USART_BaudRate   = bound;//串口波特率
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
        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);
    }
    //NVIC初始化
    {
        NVIC_InitTypeDef NVIC_InitStructure;

        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //子优先级3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
        NVIC_Init(&NVIC_InitStructure);
    }
    //开启串口中断和串口中断使能
    {
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
        USART_Cmd(USART1, ENABLE);
    }
}

引脚配置,发送引脚配置输出,接受引脚配置输入,至于模式,官方就是这么配的,直接这样就可以了,换不同的串口只需要换不同的引脚就可以了。

引脚配置可以映射,需要在引脚配置加入以下代码,前面是复用的外设名和复用的程度。Remap是全映射,PartialRemap是部分映射,

GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
串口号 没有重映像 部分重映像 全部重映像
1 A9/A10 B6/B7
2 A2/A3 D5/D6
3 B10/B11 C10/C11 D8/D9
4 C10/C11
5 C12/D2

串口配置,基本上需要配置的参数就是波特率了,两个设备要波特率和下面的其他参数一致才能通信,但是常规我们就是按照这样的参数配置,遇到特殊情况修改就是了。

中断优先级配置和前面一样。

这里我们打开串口的接收中断。那么串口每次接收到一字节的数据就会进入一次串口中断。

下面看串口中断服务函数

void USART1_IRQHandler(void)                    //串口1中断服务程序
{
    u8 Res;
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {
        Res =USART_ReceiveData(USART1);    //读取接收到的数据
        USART_SendData(USART1,Res);
    }
    USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}

首先判断这个函数是不是中断是不是由接受到数据触发的,然后使用USART_ReceiveData,就可以接受到其他设备发送过来的一字节数据,使用USART_SendData,就可以发送一个字节的数据出去,达到通讯的目的。

然后这个串口中断的效果就是一个数据回环,发什么数据给单片机,单片机就会返回什么样的数据。

如果串口想要使用printf的话,需要串口重定向

在该文件中添加以下函数就可以了

int fputc(int ch, FILE *f)
{      
    while((USART1->SR&0X40)==0){}//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
    return ch;
}

该函数需要包含头文件

#include "stdio.h"

然后还需要修改魔术棒的设置,在Target栏中选择Use MicroLIB,编译后就可以使用串口了

串口接受不定长字节数据

可以写以下函数放在串口接收中断中,以下面代码为例子,0x5A就是一帧数据的结束。如果自己使用,根据使用环境修改。

#define USART_XXX_REC_LEN           200     //定义最大接收字节数 200

uint8_t  USART_XXX_RX_BUF[USART_XXX_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
uint16_t USART_XXX_RX_STA;                //接收状态标记

void usart_XXX_receive_data(uint8_t Res)
{
    if((USART_XXX_RX_STA&0x8000)==0)//接收未完成
    {
        if(Res==0X5A)
        {
            USART_XXX_RX_STA|=0x8000;
            USART_XXX_RX_BUF[USART_XXX_RX_STA&0X3FFF]=Res ;
        }
        else
        {
            USART_XXX_RX_BUF[USART_XXX_RX_STA&0X3FFF]=Res ;
            USART_XXX_RX_STA++;
            if(USART_XXX_RX_STA>(USART_XXX_REC_LEN-1))
                USART_XXX_RX_STA=0;//接收数据错误,重新开始接收
        }
    }
}

然后再在While循环或者定时器中断中实现以下的解析代码功能

void usart_XXX_receive_data_unpacket()
{
    uint8_t uartReceiveLen;
    if(USART_XXX_RX_STA&0x8000)
    {
        //做代码解析处理
        // TODO:

        //代码解析完成后,将数组清零
        memset(USART_XXX_RX_BUF,0,sizeof(USART_XXX_RX_BUF));
        USART_XXX_RX_STA=0;
    }
}

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请附上原文出处链接及本声明。

原文链接: https://snqx-lqh.gitee.io/wiki/