STM32F407的USART功能底层解释

前言:USART这一串口通信功能在之前有过介绍,这里主要是对代码的解释与实际情况如何应用进行说明。

一、如何配置UART

步骤流程:创建GPIO,UART,NVIC结构体打开所要使用的UARTx对应的时钟与对应的GPIO的时钟;对使用的GPIO端口进行引脚复用映射;进行GPIO的初始化配置;进行UARTx的初始化配置并使能;打开UARTx的中断,设置中断触发标准位;配置中断的NVIC;写对应的中断函数。

oid Initial_UART5(unsigned long baudrate)
{
 		//GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOC,ENABLE); //使能GPIOCD时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);//使能USART5时钟
 
	//串口2对应引脚复用映射
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource12,GPIO_AF_UART5); //GPIOC12复用为USART5
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource2,GPIO_AF_UART5); //GPIOD2复用为USART5
	
	//UART5端口配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOC,&GPIO_InitStructure); //

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOD,&GPIO_InitStructure); //

   //USART2 初始化设置
	USART_InitStructure.USART_BaudRate = baudrate;//波特率设置
	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(UART5, &USART_InitStructure); //初始化串口5
	
    USART_Cmd(UART5, ENABLE);  //使能串口5
	USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);//开启相关中断,设置中断触发标志位为接收数据寄存器非空

	//UART5 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;//串口5中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=7;//抢占优先级7
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、
}

void UART5_IRQHandler(void)
{
  if(USART_GetITStatus(UART5, USART_IT_TXE) != RESET)//当接收发送数据的中断信号时
  {   
    USART_SendData(UART5, TxBuffer[TxCounter++]); //发送TX缓冲数组里面的数据
    USART_ClearITPendingBit(UART5, USART_IT_TXE);
    if(TxCounter == count) USART_ITConfig(UART5, USART_IT_TXE, DISABLE);//当收到的数据超过缓冲数组,认为采集出错,不发送数据
  }
	else if(USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)//当接收到接收数据的中断信号时
  {
		CopeSerial2Data((unsigned char)UART5->DR);//处理数据CopeSerialData为串口5中断调用函数,串口每收到一个数据,调用一次这个函数。(具体怎么处理接收到的数据变成数据包得根据实际模块的手册写)
		USART_ClearITPendingBit(UART5, USART_IT_RXNE);//清除标志位,利于下一次接收数据
  }
	USART_ClearITPendingBit(UART5,USART_IT_ORE);
}

如果一个模块它接收的数据要以数据包的形式接收才使得这段的数据有意义的话那该怎么具体配置呢。这个时候就不能使用UART_Recieve_Data()这个函数的,因为这个函数只会接收一个数据,无法接收数据包吗,和发出数据包。

在上面的代码中中断函数里面写道,当接收数据的中断标志位触发时,会执行一个CopeSerial2Data((unsigned char)UART5->DR);函数。

这个函数的作用就是根据对应的模块手册里面规定的数据格式而写的一个接收函数

struct SAcc stcAcc;
struct SGyro stcGyro;
struct SAngle stcAngle;

struct SAcc
{
    short a[3];
    short T;
};
struct SGyro
{
    short w[3];
    short T;
};
struct SAngle
{
    short Angle[2];
};


void CopeSerial2Data(unsigned char ucData)
{
    static unsigned char ucRxBuffer[250];
    static unsigned char ucRxCnt = 0;

    // LED_REVERSE();					//接收到数据,LED灯闪烁一下
    ucRxBuffer[ucRxCnt++] = ucData; //将收到的数据存入缓冲区中
    if (ucRxBuffer[0] != 0x55)      //数据头不对,则重新开始寻找0x55数据头
    {
        ucRxCnt = 0;
        return;
    }
    if (ucRxCnt < 11)
    {
        return;
    } //数据不满11个,则返回
    else
    {
        switch (ucRxBuffer[1]) //判断数据是哪种数据,然后将其拷贝到对应的结构体中,有些数据包需要通过上位机打开对应的输出后,才能接收到这个数据包的数据
        {
        // memcpy为编译器自带的内存拷贝函数,需引用"string.h",将接收缓冲区的字符拷贝到数据结构体里面,从而实现数据的解析。
        case 0x51:
            memcpy(&stcAcc, &ucRxBuffer[2], 8);
            break;
        case 0x52:
            memcpy(&stcGyro, &ucRxBuffer[2], 8);
            break;
        case 0x53:
            memcpy(&stcAngle, &ucRxBuffer[6], 2);
            break;
        }
        ucRxCnt = 0; //清空缓存区
    }
}

其中包括帧头的识别,然后对数据包的一个数据解析,将存储在数据缓存区的数据转存到你想要的结构体数组里面去,将一团数据包拆分成有意义的数据。

同样的当你需要发送数据时你只需要senddata就可以了,数据的解包由接收方来做。

所以两个模块之间用UART进行通信实际上就是相互之间发送与接收数据。只是数据的表现形式可能不是一个简单的数字,而是一个由帧头,中间数据字节,帧尾,校验位这些数据构成的数据包。在通信时就要加入解包的这个步骤就可以了。

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Young member

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

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

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

打赏作者

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

抵扣说明:

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

余额充值