目录

一、硬件资源

连接方案

其他配置

二、实验原理

基本定义

USART介绍

USART工作原理

数据发送

数据接收

蓝牙HM-10配置

三、代码部分

usart.c

usart.h

Serial.c

Serial.h

main.c

结语

一、硬件资源

STM32F401,OLED,蓝牙hm10

连接方案

设备1的TX与设备2的RX连接,这样设备1发送的数据可以被设备2接收到。

由引脚复用表,我们选择PB6和PB7分别作为TX和RX,那么PB6接蓝牙的RX,PB7接蓝牙的TX。

其他配置

可以在手机上下载一个蓝牙BLE助手,用来与STM通信。进入软件后寻找要配对的蓝牙,注意要先将手机定位开启,然后就可以收发数据了。

二、实验原理

基本定义

波特率∶串口通信的速率。因此发生方和接收方要设置相同的波特率。

波特率计算公式: baud = fck/(16* USARTDIV) 起始位 : 标志一个数据帧的开始,固定为低电平。

停止位∶用于数据帧间隔,固定为高电平。为下一次的起始位奠定基础。 数据位︰数据帧的有效载荷,1为高电平,0为低电平,低位先行

校验位:用于数据验证,根据数据位计算得来。保证数据在一定范围内的正确性。八位数据的格式

在八位的基础上可以加一位奇偶校验位

以奇校验为例,就是九位数据位最终1的个数为奇数个,比如数据为0000 1001,有偶数个1,那么校验位为1,使得最终1的个数为奇数个。

当然,并不是说八位就一定无校验位,九位就一定有校验位,只不过8位恰好为一字节,为了方便我们这样设置。

USART介绍

USART (Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2);可选校验位(无校验/奇校验/偶校验)

还可以选择硬件流控制,A发送数据,B接收数据,如果B还未处理完数据A就继续发送过来会导致数据丢失。硬件流控制就是B告诉A是否可以继续发送。

USART工作原理

简化后如下图

数据发送

发送数据时数据由数据线进入TDR,送入移位寄存器后右移(低位先行)送到TX,下一个数据发过来时要查看TXE(TX Empty)标志位,若为1,说明TDR已经空了,可以放入下一个数据。

数据接收

数据经RX进入后送至移位寄存器,先进入最高位,右移使得数据形式不变,之后进入RDR,全部进入后RXNE(RX Not Empty)置1,表示接收到数据。

由于设备不知道另一设备什么时候发数据,接收数据的方式有两种。第一,轮询模式,CPU一直查询是否有数据发来,占用CPU资源。第二,中断,有数据发来时暂停CPU任务,转而执行中断任务,涉及到中断函数和NVIC的配置。

蓝牙HM-10配置

蓝牙的设置由指令控制,具体的指令可以看配对的蓝牙说明书。

常见的设置指令如下

"AT+BAUD[P1]":设置波特率9600 "AT+PARI[P1]"):串口校验位设置 "AT+STOP[P1]:停止位设置 "AT+MODE[P1]":设置工作模式,2为透传+远控模 "AT+NAME[name]"):设置名字 "AT+ROLE[P1]" :设置主从模式,0从1主

如果要查询的话:格式为指令+"?",比如:"AT+BAUD?"用来查询蓝牙的波特率

三、代码部分

usart.c

这部分是对usart相关的配置,包括GPIO,USART和NVIC

#include "stm32f4xx.h"

#include "stdarg.h"

#include "stdio.h"

uint8_t RxFlag;

uint8_t RxData;

void Usart_init(void){

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//USART时钟初始化

GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);//实现接口的复用功能

GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);

GPIO_InitTypeDef GPIO_InitStruct;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//复用模式

GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//推挽输出

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;

GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB,&GPIO_InitStruct);

USART_InitTypeDef USART_InitStruct;

USART_InitStruct.USART_BaudRate = 9600;//波特率

USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

USART_InitStruct.USART_WordLength = USART_WordLength_8b;//字长

USART_InitStruct.USART_StopBits = USART_StopBits_1;//一位停止位

USART_InitStruct.USART_Parity = USART_Parity_No;//校验位

USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制

USART_Init(USART1, &USART_InitStruct);

USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启RXNE标志位到NVIC输出

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级为2

NVIC_InitTypeDef NVIV_InitStruct;

NVIV_InitStruct.NVIC_IRQChannel = USART1_IRQn;

NVIV_InitStruct.NVIC_IRQChannelCmd = ENABLE;

NVIV_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;

NVIV_InitStruct.NVIC_IRQChannelSubPriority = 1;

NVIC_Init(&NVIV_InitStruct);

USART_Cmd(USART1,ENABLE);

}

void Usart_SendByte(uint8_t byte){

//等TDR的数据全部进入移位寄存器

while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

USART_SendData(USART1, byte);

}

void Usart_SendArray(uint8_t* Array,uint16_t length){

for(int i=0;i

Usart_SendByte(Array[i]);

}

}

void Usart_SendString(char* str){//字符串有结束标志位,不用长度

for(int i=0;str[i] != '\0';i++){

Usart_SendByte(str[i]);

}

}

uint32_t Pow(uint32_t x){//求10的x次方

uint32_t ans=1;

for(uint8_t i=0;i

ans*=10;

}

return ans;

}

void Usart_SendNumber(uint32_t Number,uint16_t Length){//为了发送大整数

uint32_t ans=Pow(Length-1);

for(int i=0;i

Usart_SendByte(Number / ans % 10+ '0');

ans/=10;

}

}

//先勾选Use Micro LIB为keil嵌入式平台优化的一个精简库

int fputc(int ch,FILE *f){//该函数为print函数的底层,prinf()函数重定向,将打印的东西输出到串口

Usart_SendByte(ch);

return ch;

}

void Usart_Printf(char *format, ...){//参数用来接收格式化字符串,...接收可变参数列表

char String[100];

va_list arg;//参数表,类型包含在stdarg头文件

va_start(arg,format);

vsprintf(String,format,arg);

va_end(arg);//释放参数列表

Usart_SendString(String);

}

uint8_t GetRxFlag(){

if(RxFlag == 1){

RxFlag=0;

return 1;

}

return 0;

}

uint8_t GetRxData(){

return RxData;

}

void USART1_IRQHandler(){//重写中断函数

if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET){

RxData = USART_ReceiveData(USART1);

RxFlag = 1;

USART_ClearITPendingBit(USART1,USART_IT_RXNE);//对RXNE清零

}

}

usart.h

#ifndef USART_H

#define USART_H

#include

void Usart_init(void);

void Usart_SendByte(uint8_t byte);

void Usart_SendArray(uint8_t* Array,uint16_t length);

void Usart_SendString(char* str);

void Usart_SendNumber(uint32_t Number,uint16_t Length);

int fputc(int ch,FILE *f);

void Usart_Printf(char *format, ...);

uint8_t GetRxFlag(void);

uint8_t GetRxData(void);

#endif

Serial.c

这部分是对蓝牙的设置

#include "stm32f4xx.h" // Device header

#include "usart.h"

#include "Delay.h"

void Serial_Init(){

Usart_init();

Usart_SendString("AT");

Usart_SendString("AT+BAUD[0]");//设置波特率9600

Usart_SendString("AT+PARI[0]");//无串口校验

Usart_SendString("AT+STOP[0]");//一个停止位

Usart_SendString("AT+MODE[2]");//工作模式,2为透传+远控模式

Usart_SendString("AT+NAME[MLT-BT05]");

Usart_SendString("AT+ROLE[1]");//设置主从模式,0从1主

}

Serial.h

#ifndef SERIAL_H

#define SERIAL_H

void Serial_Init();

#endif

main.c

#include "stm32f4xx.h" // Device header

#include "Delay.h"

#include "oled.h"

#include "usart.h"

#include "Serial.h"

uint8_t Data;

int main(void){

OLED_Init();

Serial_Init();//其中包含了usart的初始化

OLED_ShowString(1,1,"RxData");

while(1){

//发送

Usart_SendString("Hello!\r\n");

//轮询模式

/*if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET){//不断查询RXNE标志位

Data = USART_ReceiveData (USART1);

OLED_ShowHexNum(2,1,Data,2);

}*/

//中断模式

/*if(GetRxFlag() == 1){//标志位为1,说明收到数据

Data = GetRxData();

Usart_SendByte(Data);

OLED_ShowHexNum(1,8,Data,2);

}*/

}

}

结语

这样我们就可以通过蓝牙做一些小设计了,比如控制灯的开关,手机发送给STM  'o',STM接收到数据后判断,如果为'o',则打开灯,收到 'f'则关灯。

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。