STM32硬件USART——蓝牙与手机传输数据

硬件USART

传输方式:串行传输,低位先行。

传输类型:帧传输。有校验位则9位一帧,无校验位则8位一帧。

波特率:一秒钟能传输的bit个数。

                波特率 = 外设时钟÷ 分频系数 ÷16。

                外设时钟是指外挂载总线APB1或APB2的时钟,两者不同。

                要÷16是因为在读取数据时,一位bit需要16个时钟采样,来防止/判断噪音。所以÷16得到波特率,是为了保证系统时钟能够有足够快的频率,保证对每bit的传输都×16倍时间去检测,得到真正的一位bit发送接收时间。不然假设16MHz的外设时钟,不分频得到波特率,那么实际一位的时间需要16*16Mhz的时钟才能正确发送,但这已经远远超出了外设时钟的上限(个人理解)(1MHz = 1 000 000  Hz)

停止位时间:发送一个字节数据(8bit)之后,停止位占多少个bit时钟周期(0.5,1,1.5,2),一般1个。

字节长度:8位、9位。一般8位不加校验位,9位加校验位。

DR寄存器:内分   发送数据寄存器+发送移位寄存器、接收寄存器+接收移位寄存器。

                对软件层USART传输而言,收发都是访问DR地址,不对其内部的地址进行细分。也就是一个地址两种功能,Rdata = DR就是接收;DR = Tdata就是发送。具体逻辑由硬件判断。

                发送流程:

                       ①调用函数判断发送数据移位寄存器是否空(标志位TXE—TE Empty空),判断数据有(1)没有(0)从发送数据寄存器被硬件自动转移到发送移位数据寄存器。

                       ②空的话则把数据发送给DR(赋值给DR :   DR = Tdata)。

                        ③为了下一次方便发送的话,可以在每次发送后等待TXE=1(即发送移位寄存器空),让下一次数据进来的时候不用等待,直接发送。

                接收流程:

                        ①调用函数判断接收数据寄存器是否非空(标志位TXNE — TX Not Empyt ),判断数据有(1)没有(0)从接收移位寄存器被硬件自动转移到接收数据寄存器

                        ②读取值为1(非空)的话,就要读取接收寄存器的数据(Rdata = DR)

                        ③在接收寄存器内数据时(RXNE == 1),对寄存器进行读取,会自动清除标志位

空闲状态:TX高电平

启动与发送:

                启动:TX拉低,并保持一个bit周期,在这个TX拉低开始后,会有16个时钟来采样后续的1bit周期内的TX电平,来确定这个启动是真的启动而不是电平不稳导致。

               发送:启动之后的8/9bit时间用于数据的发送,流程也是1bit时间有16个时钟采样才确定最终发送数据。

                1bit时间内的16个时钟采样吗,分别在第3、5、7、8、9、10这6个时间内采样,由8、9、10决定最终采样结果:3个相同采样电平则是采样结果,其余按2:1多数发送。

                在启动模式时,如果3个采样不是全0,而是2个0与1个1,会产生一个噪音标志位NE(NOISE ERROR)

数据发送:在1bit时间的中间时间段拉高SCL,表示发送数据。(在中间更能保证数据是稳定而非翻转不及时导致)

STM32F103C8T6所有的USART资源:

                                                        APB2:USART1;

                                                        APB1:USART2,USART3;

对应引脚( 不使用—>不考虑其他应引脚)

                                                        USART1:RX(PA10),TX(PA9);

                                                        USART2:RX(PA3),TX(PA2);

                                                        USART3:RX(PB11),TX(PB10);

配置流程:开启外设时钟——>利用结构体初始化

                ①开启USART1的外设时钟

                ②定义结构体,初始化结构体参数(波特率、收发模式、停止位时间、奇偶校验、数据字长、数据流控制(没用到?待研究))

                ③用结构体初始化USART1

                

NVIC

名字来源:Nested Vectored Interrupt Controller (嵌套向量中断控制器)

在此处的作用:外部中断–单片机作为蓝牙传输接收方接收数据需要

各英文词含义:

        ①抢占优先级–PreemptionPriority后发中断是否能插队。

        ②响应优先级–SubPriority:同发中断谁能先排队。

特别说明:同抢占优先级的中断,则看响应优先级,数值靠前(小)的优先插/排队。

        ③中断分组–NVIC_PriorityGroup_X(第X组):把16个数值分给抢占优先级、响应优先级。

        总共有3位二进制数控制分组(也就是0~15个情况),其中x表示2的x次方,指的是从0~2的x次方大小是抢占优先级的个数,在2的x次方~15是响应优先级的个数。

图片源于(stm32入门篇–中断的初步认识及其优先级和分组_中断优先级和分组-CSDN博客)

        ④中断通道--IRQChannel:中断通道是指在微控制器(MCU)中,用于处理外部或内部事件的特定路径。当某个事件发生时,微控制器会通过中断通道暂停当前正在执行的程序,转而执行与该事件相关的中断服务程序(ISR),处理完成后返回原来被暂停的位置继续运行。

配置方式:开启串口外部中断 ——> 结构体配置基础参数 ——> 传参结构体初始化NVIC

配置流程:

                ①开启USART1串口外部输入中断

                ②设置中断抢占优先级、中断通道、中断通道使能、响应优先级的分组情况

                ③设置结构体初始化参数,之后用结构体初始化

                ④开启USART1的时钟

蓝牙(DX-BT24 、XY-MB026A)

透传:把蓝牙作为数据的中转站,蓝牙接收数据后,不对数据进行任何处理,直接将数据传递给接收方。

非透传:蓝牙会对数据进行处理(加密、封装、转格式……)后再发给接收方。

组网:多个蓝牙之间可以相互联系。(?待研究)

特别注意:

                1.蓝牙只作为一个中转站,所以不需要任何代码去直接操作蓝牙。

                2.不同蓝牙的内部封装可能由于内部API不同,而导致需要不同的传输方案,故需要写传输数据的代码,将数据正确传给蓝牙。

                3.蓝牙API:蓝牙模块中的API是一组用于实现蓝牙通信功能的工具和接口。这些API允许应用程序与蓝牙设备进行交互,包括发现、连接、数据传输和设备控制等操作。

接线:USART1的TXD(PA9)接蓝牙的RXD

                              RXD(PA10)接蓝牙的TXD

实现流程:

                   ①发送——封装一个发送函数实现自定义发送

                    ②接收——在中断函数内部读取外部输入。

                由于USART的NVIC中断被配置为外部输入中断,所以一旦由外部数据的输入,就会进入中断,可以根据这个关系直接在中断函数内完成读取输入数据操作。

主要代码

USART1及其NIVC、GPIO口的初始化

void Serial_Init(void)
{
    /*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);    //开启USART1的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //开启GPIOA的时钟
    
    /*GPIO初始化*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);                    //将PA9引脚初始化为复用推挽输出
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);                    //将PA10引脚初始化为上拉输入
    
    /*USART初始化*/
    USART_InitTypeDef USART_InitStructure;                    //定义结构体变量
    USART_InitStructure.USART_BaudRate = 9600;                //波特率
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    //硬件流控制,不需要
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;    //模式,发送模式和接收模式均选择
    USART_InitStructure.USART_Parity = USART_Parity_No;        //奇偶校验,不需要
    USART_InitStructure.USART_StopBits = USART_StopBits_1;    //停止位,选择1位
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;        //字长,选择8位
    USART_Init(USART1, &USART_InitStructure);                //将结构体变量交给USART_Init,配置USART1
    
    /*中断输出配置*/
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);            //开启串口接收数据的中断
    
    /*NVIC中断分组*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);            //配置NVIC为分组2
    
    /*NVIC配置*/
    NVIC_InitTypeDef NVIC_InitStructure;                    //定义结构体变量
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;        //选择配置NVIC的USART1线
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //指定NVIC线路使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;        //指定NVIC线路的抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;        //指定NVIC线路的响应优先级为1
    NVIC_Init(&NVIC_InitStructure);                            //将结构体变量交给NVIC_Init,配置NVIC外设
    
    /*USART使能*/
    USART_Cmd(USART1, ENABLE);                                //使能USART1,串口开始运行
}

发送一个字节数据

void Serial_SendByte(uint8_t Byte)
{
    USART_SendData(USART1, Byte); //将字节数据写入数据寄存器,写入后USART自动生成时序波形
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);    //等待发送完成
    /*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

接收一个字节数据+USART1中断函数

void USART1_IRQHandler(void)
{
    if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)        //判断是否是USART1的接收事件触发的中断
    {
        Serial_RxData = USART_ReceiveData(USART1);                //读取数据寄存器,存放在接收的数据变量
        Serial_RxFlag = 1;                                        //置接收标志位变量为1
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);            //清除USART1的RXNE标志位
                                                                //读取数据寄存器会自动清除此标志位
                                                                //如果已经读取了数据寄存器,也可以不执行此代码
    }
}

作者:QL.ql

物联沃分享整理
物联沃-IOTWORD物联网 » STM32硬件USART——蓝牙与手机传输数据

发表回复