打印
[其他ST产品]

STM32的AFIO时钟什么时候需要开启

[复制链接]
楼主: 自动化陈稳
手机看帖
扫描二维码
随时随地手机跟帖
21
自动化陈稳|  楼主 | 2023-5-27 13:19 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
USART通用同步异步收发器
3.1 USART简介

        通用同步异步收发器 (USART) 能够灵活地与外部设备进行全双工数据交换,满足外部设备对 工业标准 NRZ 异步串行数据格式的要求。USART 通过小数波特率发生器提供了多种波特率。它支持同步单向通信和半双工单线通信;还支持 LIN(局域互连网络)、智能卡协议与 IrDA (红外线数据协会)SIR ENDEC 规范,以及调制解调器操作 (CTS/RTS)。而且,它还支持 多处理器通信。通过配置多个缓冲区使用 DMA 可实现高速数据通信。

使用特权

评论回复
22
自动化陈稳|  楼主 | 2023-5-27 13:19 | 只看该作者
USART功能

接口通过三个引脚从外部连接到其他设备。

任何USART双向通信均需要至少两个引脚:接收数据输入引脚(RX)和发送数据输出引脚(TX);

其中过采样技术可区分有效输入数据和噪音,从而用于恢复数据。

正常USART模式下,通过以下引脚以帧的形式发送和接收串行数据:

1. 发送或接收前保持空闲线路

2. 起始位

3. 数据(字长8位或者9位),最低有效位在前

4. 用于指示帧传输已完成的0.5个、1个、1.5个、2个停止位

5. 该接口使用小数波特率发生器-带12位尾数和4位小数

6. 状态寄存器(USART_SR)

7. 数据寄存器(USART_DR)

8. 波特率寄存器(USART_BRR)

9. 智能卡模式下的保护时间寄存器(USART_GTPR)

使用特权

评论回复
23
自动化陈稳|  楼主 | 2023-5-27 13:19 | 只看该作者
同步模式下需要:SCLK:发送器时钟输出。该引脚用于输出发送器数据时钟,以便按照 SPI 主模式进行同步发送。

nCTS:“清除已发送”用于在当前传输结束时阻止数据发送(高电平时)

nRTS:“请求已发送”用于指示USART已准备好接收数据(低电平时)

使用特权

评论回复
24
自动化陈稳|  楼主 | 2023-5-27 13:19 | 只看该作者
3.3 USART框图



使用特权

评论回复
25
自动化陈稳|  楼主 | 2023-5-27 13:20 | 只看该作者
4. STM32串口寄存器配置方法

常用的串口相关寄存器:

        USART_SR:状态寄存器

        USART_DR:数据寄存器

        USART_BRR:波特率寄存器

使用特权

评论回复
26
自动化陈稳|  楼主 | 2023-5-27 13:20 | 只看该作者
4.1 USART_SR 状态寄存器

USART_SR:状态寄存器  Status register

状态寄存器主要是操作其0-9位,

接下来介绍几个比较重要的状态标志位:

①P0:PE(奇偶校验错误)Parity error

0:无奇偶校验错误  1:奇偶校验错误

②P5:RXNE(读取数据寄存器不为空)Read data regiter not empty

当RDR移位寄存器的内容已经传输到USART_DR寄存器时,该位由硬件置1;  0:未接收到数据  1:已准备好读取接收到的数据

使用特权

评论回复
27
自动化陈稳|  楼主 | 2023-5-27 13:20 | 只看该作者
③P6:TC(发送完成)Transmission complete

如果已完成对TX的发送,则该位由硬件置1; 0:数据未传输到移位寄存器  1:数据传输到移位寄存器

④P7:TXE(发送数据寄存器为空)Transmit data register empty

当TDR寄存器的内容已传输到移位寄存器中,该位由硬件置1;(该位的作用同P5_RXNE 判断发送是否成功)

使用特权

评论回复
28
自动化陈稳|  楼主 | 2023-5-27 13:20 | 只看该作者
4.2 USART_DR 数据寄存器

USART_DR:数据寄存器   Data register

位 8:0 DR[8:0]:

        数据值包含接收到数据字符或已发送的数据字符(我们想要写的程序是存储在数据寄存器中的),具体取决于所执行的操作是“读取”操作还是“写入”操作。

        因为数据寄存器包含两个寄存器,一个用于发送 (TDR),一个用于接收 (RDR),因此它具有 双重功能(读和写)。

        TDR 寄存器在内部总线和输出移位寄存器之间提供了并行接口。

        RDR 寄存器在输入移位寄存器和内部总线之间提供了并行接口。

        在使能奇偶校验位的情况下(USART_CR1 寄存器中的 PCE 位被置 1)进行发送时,由于 MSB 的写入值(位 7 或位 8,具体取决于数据长度)会被奇偶校验位所取代,因此该值不 起任何作用。

        在使能奇偶校验位的情况下进行接收时,从 MSB 位中读取的值为接收到的奇偶校验位。

使用特权

评论回复
29
自动化陈稳|  楼主 | 2023-5-27 13:21 | 只看该作者
4.3 USART_BRR 波特率寄存器

USART_BRR:波特率寄存器   Baud rate register

位 15:4 DIV_Mantissa[11:0]:

USARTDIV 的尾数这 12 个位用于定义 USART 除数 (USARTDIV) 的尾数(整数)

位 3:0 DIV_Fraction[3:0]:

USARTDIV 的小数这 4 个位用于定义 USART 除数 (USARTDIV) 的小数(小数)。当 OVER8 = 1 时,不考虑 DIV_Fraction3 位,且必须将该位保持清零。

注:位 15:4的意思是第4-15位; 位3:0的意思是第0-3位;

使用特权

评论回复
30
自动化陈稳|  楼主 | 2023-5-27 13:21 | 只看该作者

使用特权

评论回复
31
自动化陈稳|  楼主 | 2023-5-27 13:22 | 只看该作者
4.3.1 如何配置波特率寄存器USART_BRR

首先,先来介绍一下STM32F4波特率的计算(过采样OVER8=0):


使用特权

评论回复
32
自动化陈稳|  楼主 | 2023-5-27 13:22 | 只看该作者
整个公式,只要我们知道了USARTDIV就可以计算出波特率;同样的,我们知道了波特率,也可以反过来求USARTDIV;

到这里,我们需要明确我们求波特率的目的是什么?明确了目的我们才能知道究竟是正着求波特率,还是反过来求USARTDIV;(这很重要)

我们希望通过USARTDIV得到串口USART_BRR寄存器的值。通过上述公式计算出USARTDIV;

ag. 假设我们串口1设置波特率115200,PCLK2的时钟(APB2总线时钟频率)为84M(通过时钟的学习,我们知道总线时钟为168M,APB2通过分频器得到的时钟频率为168M/2=84M),因此:

        USARTDIV=84000000/(115200*16)=45.572

使用特权

评论回复
33
自动化陈稳|  楼主 | 2023-5-27 13:22 | 只看该作者
再次看这个图:通过波特率寄存器计算出的值会返回到发送数据寄存器TDR和接受数据寄存器RDR,作为发送和接收的波特率;

所以最终得到DIV_Fraction=16*0.572=9=0X09;(小数部分)

                     DIV_Mantissa=45=0X2D;(整数部分)

使用特权

评论回复
34
自动化陈稳|  楼主 | 2023-5-27 13:22 | 只看该作者
照应上部分波特率寄存器的配置:
位 15:4 DIV_Mantissa[11:0]:

USARTDIV 的尾数这 12 个位用于定义 USART 除数 (USARTDIV) 的尾数(整数)

位 3:0 DIV_Fraction[3:0]:

USARTDIV 的小数这 4 个位用于定义 USART 除数 (USARTDIV) 的小数(小数)。当 OVER8 = 1 时,不考虑 DIV_Fraction3 位,且必须将该位保持清零。

注:位 15:4的意思是第4-15位; 位3:0的意思是第0-3位;

使用特权

评论回复
35
自动化陈稳|  楼主 | 2023-5-27 13:23 | 只看该作者
5. 串口通信程序实现(发送什么,就接收什么)
1. 串口时钟使能:RCC_APBxPeriphClockCmd();    GPIO时钟使能:RCC_AHB1PeriphClockCmd();

2. 引脚复用映射:GPIO_PinAFConfig();

3. GPIO端口模式设置:GPIO_Init();  模式设置:GPIO_Mode_AF;

4. 串口参数初始化:USART_Init();

5. 开启中断并且初始化NVIC:NVIC_Init();     USART_ITConfig();

6. 使能串口:USART_Cmd();

7. 编写中断服务函数:USARTx_IQRHandler();

8. 串口数据收发:void USART_SendData();    uint16_t USART_ReceiveData();

9. 串口传输状态获取:FlagStatic USART_GetFlagStatus();    void USART_ClearITPendingBit();

#include "stm32f4xx.h"
#include "delay.h"
#include "LED.h"
#include "BEEP.h"
#include "Key.h"
#include "usart.h"


void My_USART1_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;//设置GPIOA结构体变量
        USART_InitTypeDef USART_InitStructure;//设置串口结构体变量
        NVIC_InitTypeDef NVIC_InitStructure;//设置中断优先级NVIC结构体变量
       
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能串口1时钟
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//GPIOA使能
       
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);//PA9引脚映射为串口1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//PA10引脚映射为串口1
       
       
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//初始化引脚
        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_100MHz;//速度
        GPIO_Init(GPIOA,&GPIO_InitStructure);//GPIOA初始化
       
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//初始化引脚
        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_100MHz;//速度
        GPIO_Init(GPIOA,&GPIO_InitStructure);//GPIOA初始化
       
       
        USART_InitStructure.USART_BaudRate=115200;//设置波特率
        USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//设置串口模式使能Tx/Rx
        USART_InitStructure.USART_Parity=USART_Parity_No;//设置奇偶校验位
        USART_InitStructure.USART_StopBits=USART_StopBits_1;//设置停止位
        USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制
        USART_InitStructure.USART_WordLength=USART_WordLength_8b;//设置8位字长(设置9位字长通常最后一位是奇偶校验位)
        USART_Init(USART1,&USART_InitStructure);//串口初始化
        USART_Cmd(USART1,ENABLE);//使能串口1
       
        USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启相关中断   USART_IT_RXNE使能非空
        NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//串口1中断通道
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//IQR通道使能
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级1
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//子优先级1
        NVIC_Init(&NVIC_InitStructure);//初始化NVIC中断优先级
       
       
       
}
void USART1_IRQHandler(void)//中断服务函数
{
        u8 res;
        if(USART_GetITStatus(USART1, USART_IT_RXNE))//判断开启的中断是否接收到了相关信息,当该寄存器是1时,表示有数据接收到了
        {
                res=USART_ReceiveData(USART1);//将串口1接收到的数据给res
                USART_SendData(USART1,res);//将res在发送给串口1
        }
}
int main()
{
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组,2位抢占优先级,2位响应优先级
        My_USART1_Init();
        while(1)
        {
               
        }

}





使用特权

评论回复
36
自动化陈稳|  楼主 | 2023-5-27 13:23 | 只看该作者
串口调试助手:

使用特权

评论回复
37
自动化陈稳|  楼主 | 2023-5-27 13:23 | 只看该作者
5.1 STM32串口通信出现乱码

STM32在串口通信实验的过程中:可能会出现串口调试助手出现乱码的现象;

原因如下:

1. 串口发送的数据线损坏。(这种可能微乎其微)

2. 串口通信是异步通信,双方设定的通信准则不一致;可能是双方通信的波特率不一致;

3. STM32F4库函数:stm32f4xx.h设定的波特率是25(系统默认设置的),而开发板的外部时钟跟系统库函数设定的不一致,进而串口通信时出现乱码。需要根据自己开发板的外部时钟进行更改。

使用特权

评论回复
38
自动化陈稳|  楼主 | 2023-5-27 13:23 | 只看该作者
解决方法:

首先查看自己开发板的外部时钟是多少?打开stm32f4xx.h头文件大概123行,将系统默认的25000000改为自己开发板的外部时钟大小即可;

使用特权

评论回复
39
自动化陈稳|  楼主 | 2023-5-27 13:24 | 只看该作者

使用特权

评论回复
40
自动化陈稳|  楼主 | 2023-5-27 13:24 | 只看该作者
6. STM32串口通信程序
6.1 usart 中断服务函数精讲

void USART1_IRQHandler(void)                        //串口1中断服务程序
{
        u8 Res;//设置8位Res,用来接收存储的数据,作为中间变量,进行数据的发送与接收
        if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)  USART_IT_RXNE 表示读取数据寄存器不为空
        {//RESET库函数定义为0,SET为1,SER=!RESET
                //USART_GetITStatus表示获取中断状态标志位;
                //判断如果获取的中断状态标志位不为0,则if判断语句为真,则执行下述程序
                Res =USART_ReceiveData(USART1);//(USART1->DR);        //读取接收到的数据给Res
               
                if((USART_RX_STA&0x8000)==0)//接收未完成
                        //USART_RX_STA为16位状态标志位,其中第15位和第16位分别为状态标志位和停止位,置1表示停止和完成接收
                //(USART_RX_STA&0x8000)==0表示将USART_RX_STA的最高位,也就是16位拿出来,如果等于0,表示接收未完成,继续接收
                {
                        //收到继续接收的指令后,紧接着需要判断第15位是否为1,也就是是否已经接收到了0x0D;
                        if(USART_RX_STA&0x4000)//接收到了0x0d
                                //USART_RX_STA&0x4000表示将第15位拿出来判断是否为1
                        {
                                if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
                                //如果第15位已经接收到了0x0D,那么协议规定第16位为0x0A,如果没有接收到0x0A,USART_RX_STA状态标志位置0
                                else USART_RX_STA|=0x8000;        //接收完成了
                                //否则,表示接收到了,将第16位置1
                        }
                        else //还没收到0X0D
                        {       
                                if(Res==0x0d)USART_RX_STA|=0x4000;//如果接收到的数据为0x0D,则将第15位置1
                                else//如果没有接收到0x0D,则继续在0-14位接收数据
                                {
                                        USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;//将Res接收到的数据存储到BUF中,存储的字节为
                                        //USART_RX_STA&0X3FFF前14位;
                                        USART_RX_STA++;//每存储一位,状态位++,表示前0-14位一直在存储数据
                                        if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收          表示存储的位数超过了数据位
                                        //USART_RX_STA>(USART_REC_LEN-1)表示前0-14的数据位++,存储量大于USART_REC_LEN-1,超过存储的字节长,状态位置0
                                        //USART_REC_LEN-1是因为最后一位是换行符
                                }                 
                        }
                }                    
  }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则