目录
一、硬件资源
连接方案
其他配置
二、实验原理
基本定义
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'则关灯。 相关文章
发表评论