赤兔板CH32V307蓝牙模块教程(中)
前言
这篇文章是对于之前蓝牙模块教程上(传送门)的巩固,继续介绍电脑/手机连接赤菟板的例程,通过例程让大家对于蓝牙模块的使用更加熟练。老规矩,基础薄弱的同学可以移步论坛的赤菟教程目录:传送门。本次的教程中有涉及到外接的超声波模块HC-SR04和板子自带的LCD屏幕。
辅助例程:通过DMA进行蓝牙与串口的互传
#include "debug.h"
/* Global define */
#define RXBUF_SIZE 1024 // DMA buffer size
#define size(a) (sizeof(a) / sizeof(*(a)))
/* Global Variable */
u8 TxBuffer[] = " ";
u8 RxBuffer[RXBUF_SIZE]={0};
/*******************************************************************************
* Function Name : USARTx_CFG
* Description : Initializes the USART peripheral.
* 描述 : 串口初始化
* Input : None
* Return : None
*******************************************************************************/
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART7, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* USART7 TX-->C2 RX-->C3 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //RX,输入上拉
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* USART2 TX-->A2 RX-->A3 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //RX,输入上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200; // 波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 数据位 8
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位 1
USART_InitStructure.USART_Parity = USART_Parity_No; // 无校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //使能 RX 和 TX
USART_Init(UART7, &USART_InitStructure);
DMA_Cmd(DMA2_Channel9, ENABLE); //开启接收 DMA
USART_Cmd(UART7, ENABLE); //开启UART
USART_Init(USART2, &USART_InitStructure);
DMA_Cmd(DMA1_Channel6, ENABLE); //开启接收 DMA
USART_Cmd(USART2, ENABLE); //开启UART
}
/*******************************************************************************
* Function Name : DMA_INIT
* Description : Configures the DMA.
* 描述 : DMA 初始化
* Input : None
* Return : None
*******************************************************************************/
void DMA_INIT(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
// TX DMA 初始化
DMA_DeInit(DMA2_Channel8);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&UART7->DATAR); // DMA 外设基址,需指向对应的外设
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)TxBuffer; // DMA 内存基址,指向发送缓冲区的首地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 方向 : 外设 作为 终点,即 内存 -> 外设
DMA_InitStructure.DMA_BufferSize = 0; // 缓冲区大小,即要DMA发送的数据长度,目前没有数据可发
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址自增,禁用
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增,启用
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据位宽,8位(Byte)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据位宽,8位(Byte)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 普通模式,发完结束,不循环发送
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // 优先级最高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2P,禁用M2M
DMA_Init(DMA2_Channel8, &DMA_InitStructure);
// RX DMA 初始化,环形缓冲区自动接收
DMA_DeInit(DMA2_Channel9);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&UART7->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer; // 接收缓冲区
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向 : 外设 作为 源,即 内存 <- 外设
DMA_InitStructure.DMA_BufferSize = RXBUF_SIZE; // 缓冲区长度为 RXBUF_SIZE
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式,构成环形缓冲区
DMA_Init(DMA2_Channel9, &DMA_InitStructure);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// TX DMA 初始化
DMA_DeInit(DMA1_Channel7);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DATAR); // DMA 外设基址,需指向对应的外设
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer; // DMA 内存基址,指向发送缓冲区的首地址。未指定合法地址就启动 DMA 会进 hardfault
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 方向 : 外设 作为 终点,即 内存 -> 外设
DMA_InitStructure.DMA_BufferSize = 0; // 缓冲区大小,即要 DMA 发送的数据长度,目前没有数据可发
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址自增,禁用
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增,启用
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据位宽,8位(Byte)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据位宽,8位(Byte)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 普通模式,发完结束,不循环发送
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // 优先级最高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 实际为 M2P,所以禁用 M2M
DMA_Init(DMA1_Channel7, &DMA_InitStructure);
// RX DMA 初始化,环形缓冲区自动接收
DMA_DeInit(DMA1_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer; // 接收缓冲区
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向 : 外设 作为 源,即 内存 <- 外设
DMA_InitStructure.DMA_BufferSize = RXBUF_SIZE; // 缓冲区长度为 RXBUF_SIZE
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式,构成环形缓冲区
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
}
/*******************************************************************************
* Function Name : GPIO_CFG
* Description : Initializes GPIOs.
* 描述 : GPIO 初始化
* Input : None
* Return : None
*******************************************************************************/
void GPIO_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// CH9141 配置引脚初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* BLE_sleep --> C13 BLE_AT-->A7 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/*******************************************************************************
* Function Name : uartWriteBLE
* Description : send data to BLE via UART7 向蓝牙模组发送数据
* Input : char * data data to send 要发送的数据的首地址
* uint16_t num number of data 数据长度
* Return : RESET UART7 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteBLE(char * data , uint16_t num)
{
//如上次发送未完成,返回
if(DMA_GetCurrDataCounter(DMA2_Channel8) != 0){
return RESET;
}
DMA_ClearFlag(DMA2_FLAG_TC8);
DMA_Cmd(DMA2_Channel8, DISABLE ); // 关 DMA 后操作
DMA2_Channel8->MADDR = (uint32_t)data; // 发送缓冲区为 data
DMA_SetCurrDataCounter(DMA2_Channel8,num); // 设置缓冲区长度
DMA_Cmd(DMA2_Channel8, ENABLE); // 开 DMA
return SET;
}
/*******************************************************************************
* Function Name : uartWriteBLEstr
* Description : send string to BLE via UART7 向蓝牙模组发送字符串
* Input : char * str string to send
* Return : RESET UART7 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteBLEstr(char * str)
{
uint16_t num = 0;
while(str[num])num++; // 计算字符串长度
return uartWriteBLE(str,num);
}
/*******************************************************************************
* Function Name : uartReadBLE
* Description : read some bytes from receive buffer 从接收缓冲区读出一组数据
* Input : char * buffer buffer to storage the data 用来存放读出数据的地址
* uint16_t num number of data to read 要读的字节数
* Return : int number of bytes read 返回实际读出的字节数
*******************************************************************************/
uint16_t rxBufferReadPos1 = 0; //接收缓冲区读指针
uint32_t uartReadBLE(char * buffer , uint16_t num)
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9); //计算 DMA 数据尾的位置
uint16_t i = 0;
if (rxBufferReadPos1 == rxBufferEnd){
// 无数据,返回
return 0;
}
while (rxBufferReadPos1!=rxBufferEnd && i < num){
buffer[i] = RxBuffer[rxBufferReadPos1];
i++;
rxBufferReadPos1++;
if(rxBufferReadPos1 >= RXBUF_SIZE){
// 超出缓冲区,回零
rxBufferReadPos1 = 0;
}
}
return i;
}
/*******************************************************************************
* Function Name : uartReadByteBLE
* Description : read one byte from UART buffer 从接收缓冲区读出 1 字节数据
* Input : None
* Return : char read data 返回读出的数据(无数据也返回0)
*******************************************************************************/
char uartReadByteBLE()
{
char ret;
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9);//计算 DMA 数据尾的位置
if (rxBufferReadPos1 == rxBufferEnd){
// 无数据,返回
return 0;
}
ret = RxBuffer[rxBufferReadPos1];
rxBufferReadPos1++;
if(rxBufferReadPos1 >= RXBUF_SIZE){
// 超出缓冲区,回零
rxBufferReadPos1 = 0;
}
return ret;
}
/*******************************************************************************
* Function Name : uartAvailableBLE
* Description : get number of bytes Available to read from the UART buffer 获取缓冲区中可读数据的数量
* Input : None
* Return : uint16_t number of bytes Available to readd 返回可读数据数量
*******************************************************************************/
uint16_t uartAvailableBLE()
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9);//计算 DMA 数据尾的位置
// 计算可读字节
if (rxBufferReadPos1 <= rxBufferEnd){
return rxBufferEnd - rxBufferReadPos1;
}else{
return rxBufferEnd +RXBUF_SIZE -rxBufferReadPos1;
}
}
/*******************************************************************************
* Function Name : uartWriteBlocking
* Description : send data via USART2 用USART2发送数据,阻塞式
* Input : char * data data to send 要发送的数据的首地址
* uint16_t num number of data 数据长度
* Return : RESET USART2 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteBlocking(char * data , uint16_t num)
{
//等待上次发送完成
while(DMA_GetCurrDataCounter(DMA1_Channel7) != 0){
}
DMA_ClearFlag(DMA2_FLAG_TC8);
DMA_Cmd(DMA1_Channel7, DISABLE ); // 关 DMA 后操作
DMA1_Channel7->MADDR = (uint32_t)data; // 发送缓冲区为 data
DMA_SetCurrDataCounter(DMA1_Channel7,num); // 设置缓冲区长度
DMA_Cmd(DMA1_Channel7, ENABLE); // 开 DMA
return SET;
}
/*******************************************************************************
* Function Name : uartWriteStrBlocking
* Description : send string via USART2 用USART2发送字符串,阻塞式
* Input : char * str string to send
* Return : RESET USART2 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteStrBlocking(char * str)
{
uint16_t num = 0;
while(str[num])num++; // 计算字符串长度
return uartWriteBlocking(str,num);
}
/*******************************************************************************
* Function Name : uartRead
* Description : read some bytes from receive buffer 从接收缓冲区读出一组数据
* Input : char * buffer buffer to storage the data 用来存放读出数据的地址
* uint16_t num number of data to read 要读的字节数
* Return : int number of bytes read 返回实际读出的字节数
*******************************************************************************/
uint16_t rxBufferReadPos2 = 0; //接收缓冲区读指针
uint32_t uartRead(char * buffer , uint16_t num)
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6); //计算 DMA 数据尾的位置
uint16_t i = 0;
if (rxBufferReadPos2 == rxBufferEnd){
// 无数据,返回
return 0;
}
while (rxBufferReadPos2!=rxBufferEnd && i < num){
buffer[i] = RxBuffer[rxBufferReadPos2];
i++;
rxBufferReadPos2++;
if(rxBufferReadPos2 >= RXBUF_SIZE){
// 读指针超出接收缓冲区,回零
rxBufferReadPos2 = 0;
}
}
return i;
}
/*******************************************************************************
* Function Name : uartReadByte
* Description : read one byte from UART buffer 从接收缓冲区读出 1 字节数据
* Input : None
* Return : char read data 返回读出的数据(无数据也返回0)
*******************************************************************************/
char uartReadByte()
{
char ret;
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6);//计算 DMA 数据尾的位置
if (rxBufferReadPos2 == rxBufferEnd){
// 无数据,返回
return 0;
}
ret = RxBuffer[rxBufferReadPos2];
rxBufferReadPos2++;
if(rxBufferReadPos2 >= RXBUF_SIZE){
// 读指针超出接收缓冲区,回零
rxBufferReadPos2 = 0;
}
return ret;
}
/*******************************************************************************
* Function Name : uartAvailable
* Description : get number of bytes Available to read from the UART buffer 获取缓冲区中可读数据的数量
* Input : None
* Return : uint16_t number of bytes Available to read 返回可读数据数量
*******************************************************************************/
uint16_t uartAvailable()
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6);//计算 DMA 数据尾的位置
// 计算可读字节
if (rxBufferReadPos2 <= rxBufferEnd){
return rxBufferEnd - rxBufferReadPos2;
}else{
return rxBufferEnd +RXBUF_SIZE -rxBufferReadPos2;
}
}
/*******************************************************************************
* Function Name : main
* Description : Main program.
* Input : None
* Return : None
*******************************************************************************/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
DMA_INIT();
USARTx_CFG(); /* USART INIT */
USART_DMACmd(UART7,USART_DMAReq_Tx|USART_DMAReq_Rx,ENABLE); //开启 UART7 DMA 接收和发送
USART_DMACmd(USART2,USART_DMAReq_Tx|USART_DMAReq_Rx,ENABLE); //开启 USART2 DMA 接收和发送
GPIO_CFG();
// GPIO_WriteBit(GPIOA, GPIO_Pin_7,RESET); //进入 AT
GPIO_WriteBit(GPIOC, GPIO_Pin_13,SET); //enable CH9141
Delay_Ms(1000);
while(1){
int num2 = uartAvailable(); //获取可读字节数
if (num2 > 0 ){
char buffer[1024]={"\0"};
uartRead(buffer , num2); //把数据从缓冲区读到 buffer 里
uartWriteBLE(buffer , num2); //把数据发送给 蓝牙
// uartWriteBlocking(buffer , num2); //把数据发送给 串口
}
int num1 = uartAvailableBLE();
if (num1 > 0 ){
char buffer[1024]={"\0"};
uartReadBLE(buffer , num1); //读取蓝牙传输出来的数据
uartWriteBlocking(buffer , num1); //把数据发送给 串口
// uartWriteBLE(buffer , num1); //把数据发送给 PC
}
// GPIO_WriteBit(GPIOA, GPIO_Pin_7,SET); // 退出AT。可用手机或电脑连接CH9141,测试数据收发
}
}
关于DMA
简单的程序也许可以直接传送数据,但是如果涉及到多个模块间同时工作,还是建议通过DMA,使用DMA不存在丢失数据的危险还降低了CPU使用率,论坛中也有简单介绍DMA的教程:
通过CH32V307数据手册可以看出蓝牙传输数据通过的是UART7,正常初始化就行了,而使用DMA进行WCHLink串口进行数据传输要通过UART2,而非UART1,对于CPU传输串口数据本身库里面自带输出函数(即printf函数),这个例程也同样提供了初始化UART2的函数供参考(copy)。
PS:注意跳线帽位置,如果要使用CPU直接传输或者说使用printf函数就接UART1,而想使用该例程中的通过DMA进行数据传输,请接UART2。
详细介绍
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART7, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* USART7 TX-->C2 RX-->C3 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //RX,输入上拉
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* USART2 TX-->A2 RX-->A3 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //RX,输入上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200; // 波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 数据位 8
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位 1
USART_InitStructure.USART_Parity = USART_Parity_No; // 无校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //使能 RX 和 TX
USART_Init(UART7, &USART_InitStructure);
DMA_Cmd(DMA2_Channel9, ENABLE); //开启接收 DMA
USART_Cmd(UART7, ENABLE); //开启UART
USART_Init(USART2, &USART_InitStructure);
DMA_Cmd(DMA1_Channel6, ENABLE); //开启接收 DMA
USART_Cmd(USART2, ENABLE); //开启UART
}
/*******************************************************************************
* Function Name : DMA_INIT
* Description : Configures the DMA.
* 描述 : DMA 初始化
* Input : None
* Return : None
*******************************************************************************/
void DMA_INIT(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
// TX DMA 初始化
DMA_DeInit(DMA2_Channel8);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&UART7->DATAR); // DMA 外设基址,需指向对应的外设
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)TxBuffer; // DMA 内存基址,指向发送缓冲区的首地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 方向 : 外设 作为 终点,即 内存 -> 外设
DMA_InitStructure.DMA_BufferSize = 0; // 缓冲区大小,即要DMA发送的数据长度,目前没有数据可发
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址自增,禁用
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增,启用
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据位宽,8位(Byte)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据位宽,8位(Byte)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 普通模式,发完结束,不循环发送
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // 优先级最高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2P,禁用M2M
DMA_Init(DMA2_Channel8, &DMA_InitStructure);
// RX DMA 初始化,环形缓冲区自动接收
DMA_DeInit(DMA2_Channel9);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&UART7->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer; // 接收缓冲区
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向 : 外设 作为 源,即 内存 <- 外设
DMA_InitStructure.DMA_BufferSize = RXBUF_SIZE; // 缓冲区长度为 RXBUF_SIZE
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式,构成环形缓冲区
DMA_Init(DMA2_Channel9, &DMA_InitStructure);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// TX DMA 初始化
DMA_DeInit(DMA1_Channel7);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DATAR); // DMA 外设基址,需指向对应的外设
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer; // DMA 内存基址,指向发送缓冲区的首地址。未指定合法地址就启动 DMA 会进 hardfault
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 方向 : 外设 作为 终点,即 内存 -> 外设
DMA_InitStructure.DMA_BufferSize = 0; // 缓冲区大小,即要 DMA 发送的数据长度,目前没有数据可发
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址自增,禁用
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增,启用
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据位宽,8位(Byte)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据位宽,8位(Byte)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 普通模式,发完结束,不循环发送
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // 优先级最高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 实际为 M2P,所以禁用 M2M
DMA_Init(DMA1_Channel7, &DMA_InitStructure);
// RX DMA 初始化,环形缓冲区自动接收
DMA_DeInit(DMA1_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer; // 接收缓冲区
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向 : 外设 作为 源,即 内存 <- 外设
DMA_InitStructure.DMA_BufferSize = RXBUF_SIZE; // 缓冲区长度为 RXBUF_SIZE
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式,构成环形缓冲区
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
}
/*******************************************************************************
* Function Name : GPIO_CFG
* Description : Initializes GPIOs.
* 描述 : GPIO 初始化
* Input : None
* Return : None
*******************************************************************************/
void GPIO_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// CH9141 配置引脚初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* BLE_sleep --> C13 BLE_AT-->A7 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// GPIO_Init(GPIOA, &GPIO_InitStructure);
}
开始这一部分函数是初始化UART、DMA和GPIO口的,为了方便我将蓝牙和串口的合并了起来,如果想要单独的部分可以对应上篇蓝牙教程对应分离。
/*******************************************************************************
* Function Name : uartWriteBLE
* Description : send data to BLE via UART7 向蓝牙模组发送数据
* Input : char * data data to send 要发送的数据的首地址
* uint16_t num number of data 数据长度
* Return : RESET UART7 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteBLE(char * data , uint16_t num)
{
//如上次发送未完成,返回
if(DMA_GetCurrDataCounter(DMA2_Channel8) != 0){
return RESET;
}
DMA_ClearFlag(DMA2_FLAG_TC8);
DMA_Cmd(DMA2_Channel8, DISABLE ); // 关 DMA 后操作
DMA2_Channel8->MADDR = (uint32_t)data; // 发送缓冲区为 data
DMA_SetCurrDataCounter(DMA2_Channel8,num); // 设置缓冲区长度
DMA_Cmd(DMA2_Channel8, ENABLE); // 开 DMA
return SET;
}
/*******************************************************************************
* Function Name : uartWriteBLEstr
* Description : send string to BLE via UART7 向蓝牙模组发送字符串
* Input : char * str string to send
* Return : RESET UART7 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteBLEstr(char * str)
{
uint16_t num = 0;
while(str[num])num++; // 计算字符串长度
return uartWriteBLE(str,num);
}
/*******************************************************************************
* Function Name : uartReadBLE
* Description : read some bytes from receive buffer 从接收缓冲区读出一组数据
* Input : char * buffer buffer to storage the data 用来存放读出数据的地址
* uint16_t num number of data to read 要读的字节数
* Return : int number of bytes read 返回实际读出的字节数
*******************************************************************************/
uint16_t rxBufferReadPos1 = 0; //接收缓冲区读指针
uint32_t uartReadBLE(char * buffer , uint16_t num)
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9); //计算 DMA 数据尾的位置
uint16_t i = 0;
if (rxBufferReadPos1 == rxBufferEnd){
// 无数据,返回
return 0;
}
while (rxBufferReadPos1!=rxBufferEnd && i < num){
buffer[i] = RxBuffer[rxBufferReadPos1];
i++;
rxBufferReadPos1++;
if(rxBufferReadPos1 >= RXBUF_SIZE){
// 超出缓冲区,回零
rxBufferReadPos1 = 0;
}
}
return i;
}
/*******************************************************************************
* Function Name : uartReadByteBLE
* Description : read one byte from UART buffer 从接收缓冲区读出 1 字节数据
* Input : None
* Return : char read data 返回读出的数据(无数据也返回0)
*******************************************************************************/
char uartReadByteBLE()
{
char ret;
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9);//计算 DMA 数据尾的位置
if (rxBufferReadPos1 == rxBufferEnd){
// 无数据,返回
return 0;
}
ret = RxBuffer[rxBufferReadPos1];
rxBufferReadPos1++;
if(rxBufferReadPos1 >= RXBUF_SIZE){
// 超出缓冲区,回零
rxBufferReadPos1 = 0;
}
return ret;
}
/*******************************************************************************
* Function Name : uartAvailableBLE
* Description : get number of bytes Available to read from the UART buffer 获取缓冲区中可读数据的数量
* Input : None
* Return : uint16_t number of bytes Available to readd 返回可读数据数量
*******************************************************************************/
uint16_t uartAvailableBLE()
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9);//计算 DMA 数据尾的位置
// 计算可读字节
if (rxBufferReadPos1 <= rxBufferEnd){
return rxBufferEnd - rxBufferReadPos1;
}else{
return rxBufferEnd +RXBUF_SIZE -rxBufferReadPos1;
}
}
初始化函数接下来的就是蓝牙模块读取和写入的函数,他的函数名区别于串口的在于函数名以BLE结尾,具体介绍请参照上篇教程,不在赘述。
/*******************************************************************************
* Function Name : uartWriteBlocking
* Description : send data via USART2 用USART2发送数据,阻塞式
* Input : char * data data to send 要发送的数据的首地址
* uint16_t num number of data 数据长度
* Return : RESET USART2 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteBlocking(char * data , uint16_t num)
{
//等待上次发送完成
while(DMA_GetCurrDataCounter(DMA1_Channel7) != 0){
}
DMA_ClearFlag(DMA2_FLAG_TC8);
DMA_Cmd(DMA1_Channel7, DISABLE ); // 关 DMA 后操作
DMA1_Channel7->MADDR = (uint32_t)data; // 发送缓冲区为 data
DMA_SetCurrDataCounter(DMA1_Channel7,num); // 设置缓冲区长度
DMA_Cmd(DMA1_Channel7, ENABLE); // 开 DMA
return SET;
}
/*******************************************************************************
* Function Name : uartWriteStrBlocking
* Description : send string via USART2 用USART2发送字符串,阻塞式
* Input : char * str string to send
* Return : RESET USART2 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteStrBlocking(char * str)
{
uint16_t num = 0;
while(str[num])num++; // 计算字符串长度
return uartWriteBlocking(str,num);
}
/*******************************************************************************
* Function Name : uartRead
* Description : read some bytes from receive buffer 从接收缓冲区读出一组数据
* Input : char * buffer buffer to storage the data 用来存放读出数据的地址
* uint16_t num number of data to read 要读的字节数
* Return : int number of bytes read 返回实际读出的字节数
*******************************************************************************/
uint16_t rxBufferReadPos2 = 0; //接收缓冲区读指针
uint32_t uartRead(char * buffer , uint16_t num)
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6); //计算 DMA 数据尾的位置
uint16_t i = 0;
if (rxBufferReadPos2 == rxBufferEnd){
// 无数据,返回
return 0;
}
while (rxBufferReadPos2!=rxBufferEnd && i < num){
buffer[i] = RxBuffer[rxBufferReadPos2];
i++;
rxBufferReadPos2++;
if(rxBufferReadPos2 >= RXBUF_SIZE){
// 读指针超出接收缓冲区,回零
rxBufferReadPos2 = 0;
}
}
return i;
}
/*******************************************************************************
* Function Name : uartReadByte
* Description : read one byte from UART buffer 从接收缓冲区读出 1 字节数据
* Input : None
* Return : char read data 返回读出的数据(无数据也返回0)
*******************************************************************************/
char uartReadByte()
{
char ret;
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6);//计算 DMA 数据尾的位置
if (rxBufferReadPos2 == rxBufferEnd){
// 无数据,返回
return 0;
}
ret = RxBuffer[rxBufferReadPos2];
rxBufferReadPos2++;
if(rxBufferReadPos2 >= RXBUF_SIZE){
// 读指针超出接收缓冲区,回零
rxBufferReadPos2 = 0;
}
return ret;
}
/*******************************************************************************
* Function Name : uartAvailable
* Description : get number of bytes Available to read from the UART buffer 获取缓冲区中可读数据的数量
* Input : None
* Return : uint16_t number of bytes Available to read 返回可读数据数量
*******************************************************************************/
uint16_t uartAvailable()
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6);//计算 DMA 数据尾的位置
// 计算可读字节
if (rxBufferReadPos2 <= rxBufferEnd){
return rxBufferEnd - rxBufferReadPos2;
}else{
return rxBufferEnd +RXBUF_SIZE -rxBufferReadPos2;
}
}
最后是WCHLink串口读取和写入的部分,该部分使用的逻辑与之前蓝牙读取和写入的逻辑相差不大。
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
DMA_INIT();
USARTx_CFG(); /* USART INIT */
USART_DMACmd(UART7,USART_DMAReq_Tx|USART_DMAReq_Rx,ENABLE); //开启 UART7 DMA 接收和发送
USART_DMACmd(USART2,USART_DMAReq_Tx|USART_DMAReq_Rx,ENABLE); //开启 USART2 DMA 接收和发送
GPIO_CFG();
// GPIO_WriteBit(GPIOA, GPIO_Pin_7,RESET); //进入 AT
GPIO_WriteBit(GPIOC, GPIO_Pin_13,SET); //enable CH9141
Delay_Ms(1000);
while(1){
int num2 = uartAvailable(); //获取可读字节数
if (num2 > 0 ){
char buffer[1024]={"\0"};
uartRead(buffer , num2); //把数据从缓冲区读到 buffer 里
uartWriteBLE(buffer , num2); //把数据发送给 蓝牙
// uartWriteBlocking(buffer , num2); //把数据发送给 串口
}
int num1 = uartAvailableBLE();
if (num1 > 0 ){
char buffer[1024]={"\0"};
uartReadBLE(buffer , num1); //读取蓝牙传输出来的数据
uartWriteBlocking(buffer , num1); //把数据发送给 串口
// uartWriteBLE(buffer , num1); //把数据发送给 PC
}
// GPIO_WriteBit(GPIOA, GPIO_Pin_7,SET); // 退出AT。可用手机或电脑连接CH9141,测试数据收发
}
}
主函数仅为初始化之后进行先读取DMA的数据在发给对方。
效果演示
先连接蓝牙虚拟串口
分别设置好波特率115200,打开串口,发送信息,接收完毕!
本次主要例程的组成部分
LCD屏幕
该部分沿用上篇内容,没有变化,仅使用显示字符串的函数作为显示当前的距离的作用。
超声波模块
利用声音测距,声音在空气中的速度是340m/s(15℃)
当声音传播时,若遇到障碍物时,就会被反弹回来,通过计时反弹回来的时间就可以计算出从发射端到障碍物的距离——S=v*(t/2)。
端口功能 | 引脚功能 | 功能 | 赤菟板 |
HC-SR04 | VCC | 电源接5V | 3.3V |
| TRIG | 触发控制信号输入 | PB0 |
| ECHO | 回响信号输出 | PB1 |
| GND | 地线 | GND |
- 工作原理
- 给脉冲触发引脚(trig)输入一个长大于10us的高电平方波;
- 输入方波后,模块会自动发射8个40KHz的声波,与此同时回波引脚echo端的电平会由0变为1;
- 当超声波返回被模块接收到时,回波引脚端的电平会由1变为0;记下的这个时间即为超声波由发射到返回的总时长;
- 根据声音在空气中的速度为340米/秒,即可计算出所测的距离。
超声波时序图
PS:由于声波速度受温度影响,距离仅为大概值非精确值。
hc-sr04.h
#ifndef __HCSR04_H
#define __HCSR04_H
#include "debug.h"
//超声波模块引脚
#define TRIG_PORT GPIOB
#define ECHO_PORT GPIOB
#define TRIG_PIN GPIO_Pin_0 //TRIG-发送引脚PB0
#define ECHO_PIN GPIO_Pin_1 //ECHO-接收引脚PB1
//超声波模块初始化
void sr04_init(void);
//超声波模块测距
int32_t sr04_get_distance(void);
#endif
作为头文件选择赤菟板的扩展口PB1和PB0分别作为超声波模块HC-SR04的ECHO接收口和TRIG发送口。
hc-sr04.c
#include "hc-sr04.h"
#include "debug.h"
#include "lcd.h"
uint32_t d; //计算出的距离
//超声波初始化函数
void sr04_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//时钟使能
GPIO_InitStructure.GPIO_Pin = TRIG_PIN; //PB0接TRIG
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度
GPIO_Init(TRIG_PORT, &GPIO_InitStructure); //初始化GPIO
GPIO_InitStructure.GPIO_Pin = ECHO_PIN; //PB1接ECH0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(ECHO_PORT,&GPIO_InitStructure); //初始化GPIO
GPIO_ResetBits(TRIG_PORT,TRIG_PIN);//PB0初始状态为低电平,看时序图
}
/*获取距离*/
int32_t sr04_get_distance(void)
{
uint32_t t;
//PB0高电平
GPIO_SetBits(TRIG_PORT,TRIG_PIN);
delay_us(20); //持续10us以上
GPIO_ResetBits(TRIG_PORT,TRIG_PIN);//PB0低电平
//while(!GPIO_ReadInputDataBit(ECHO_PORT,ECHO_PIN)); //等待高电平
//等待PB1出现高电平
t=0;
while(!GPIO_ReadInputDataBit(ECHO_PORT,ECHO_PIN))
{
//超时处理
t++;
delay_us(9);
//如果超时,就返回一个错误码
if(t >= 10000)
return -1;
}
//测量高电平的时间
t=0;
while(GPIO_ReadInputDataBit(ECHO_PORT,ECHO_PIN))
{
t++;
delay_us(10);
//如果超时,就返回一个错误码
if(t >= 10000)
return -2;
}
//由于测量的时间,就是超声波从发射到返回的时间
d = t*0.34/2;
return d;
}
注意点:
这段程序所得到的距离值单位是cm;
确保GPIO口初始化定义是对的,是你选择的口,由于他起始信号发射的同时ECHO引脚就会被拉高,如果你明明发出了起始信号,ECHO引脚未被拉高,程序陷入while的死循环,请检查定义。
vsnprintf函数
之前的例程值向蓝牙传输的都是固定值,而此次超声波模块检测到的距离是随时变化的,所以要将变化的字符串封装在一个数组中传输过去。
char buffer[100];
void mon_log(char* format, ...)
{
va_list vArgList;
va_start(vArgList, format);
vsnprintf(buffer, 100, format, vArgList);/*把可变参数表中的数据转成字符存到buffer中,每个参数间用','隔开 */
va_end(vArgList);
}
主程序
#include "debug.h"
#include "lcd.h"
#include "hc-sr04.h"
#include "stdarg.h"
/* Global define */
#define RXBUF_SIZE 1024 // DMA buffer size
#define size(a) (sizeof(a) / sizeof(*(a)))
/* Global Variable */
u8 TxBuffer[] = " ";
u8 RxBuffer[RXBUF_SIZE]={0};
/*******************************************************************************
* Function Name : USARTx_CFG
* Description : Initializes the USART peripheral.
* 描述 : 串口初始化
* Input : None
* Return : None
*******************************************************************************/
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART7, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* USART7 TX-->C2 RX-->C3 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //RX,输入上拉
GPIO_Init(GPIOC, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200; // 波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 数据位 8
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位 1
USART_InitStructure.USART_Parity = USART_Parity_No; // 无校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //使能 RX 和 TX
USART_Init(UART7, &USART_InitStructure);
DMA_Cmd(DMA2_Channel9, ENABLE); //开启接收 DMA
USART_Cmd(UART7, ENABLE); //开启UART
}
/*******************************************************************************
* Function Name : DMA_INIT
* Description : Configures the DMA.
* 描述 : DMA 初始化
* Input : None
* Return : None
*******************************************************************************/
void DMA_INIT(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
// TX DMA 初始化
DMA_DeInit(DMA2_Channel8);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&UART7->DATAR); // DMA 外设基址,需指向对应的外设
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)TxBuffer; // DMA 内存基址,指向发送缓冲区的首地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 方向 : 外设 作为 终点,即 内存 -> 外设
DMA_InitStructure.DMA_BufferSize = 0; // 缓冲区大小,即要DMA发送的数据长度,目前没有数据可发
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址自增,禁用
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增,启用
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据位宽,8位(Byte)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据位宽,8位(Byte)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 普通模式,发完结束,不循环发送
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // 优先级最高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2P,禁用M2M
DMA_Init(DMA2_Channel8, &DMA_InitStructure);
// RX DMA 初始化,环形缓冲区自动接收
DMA_DeInit(DMA2_Channel9);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&UART7->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer; // 接收缓冲区
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向 : 外设 作为 源,即 内存 <- 外设
DMA_InitStructure.DMA_BufferSize = RXBUF_SIZE; // 缓冲区长度为 RXBUF_SIZE
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式,构成环形缓冲区
DMA_Init(DMA2_Channel9, &DMA_InitStructure);
}
/*******************************************************************************
* Function Name : GPIO_CFG
* Description : Initializes GPIOs.
* 描述 : GPIO 初始化
* Input : None
* Return : None
*******************************************************************************/
void GPIO_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// CH9141 配置引脚初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* BLE_sleep --> C13 BLE_AT-->A7 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/*******************************************************************************
* Function Name : uartWriteBLE
* Description : send data to BLE via UART7 向蓝牙模组发送数据
* Input : char * data data to send 要发送的数据的首地址
* uint16_t num number of data 数据长度
* Return : RESET UART7 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteBLE(char * data , uint16_t num)
{
//如上次发送未完成,返回
if(DMA_GetCurrDataCounter(DMA2_Channel8) != 0){
return RESET;
}
DMA_ClearFlag(DMA2_FLAG_TC8);
DMA_Cmd(DMA2_Channel8, DISABLE ); // 关 DMA 后操作
DMA2_Channel8->MADDR = (uint32_t)data; // 发送缓冲区为 data
DMA_SetCurrDataCounter(DMA2_Channel8,num); // 设置缓冲区长度
DMA_Cmd(DMA2_Channel8, ENABLE); // 开 DMA
return SET;
}
/*******************************************************************************
* Function Name : uartWriteBLEstr
* Description : send string to BLE via UART7 向蓝牙模组发送字符串
* Input : char * str string to send
* Return : RESET UART7 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteBLEstr(char * str)
{
uint16_t num = 0;
while(str[num])num++; // 计算字符串长度
return uartWriteBLE(str,num);
}
/*******************************************************************************
* Function Name : uartReadBLE
* Description : read some bytes from receive buffer 从接收缓冲区读出一组数据
* Input : char * buffer buffer to storage the data 用来存放读出数据的地址
* uint16_t num number of data to read 要读的字节数
* Return : int number of bytes read 返回实际读出的字节数
*******************************************************************************/
uint16_t rxBufferReadPos = 0; //接收缓冲区读指针
uint32_t uartReadBLE(char * buffer , uint16_t num)
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9); //计算 DMA 数据尾的位置
uint16_t i = 0;
if (rxBufferReadPos == rxBufferEnd){
// 无数据,返回
return 0;
}
while (rxBufferReadPos!=rxBufferEnd && i < num){
buffer[i] = RxBuffer[rxBufferReadPos];
i++;
rxBufferReadPos++;
if(rxBufferReadPos >= RXBUF_SIZE){
// 超出缓冲区,回零
rxBufferReadPos = 0;
}
}
return i;
}
/*******************************************************************************
* Function Name : uartReadByteBLE
* Description : read one byte from UART buffer 从接收缓冲区读出 1 字节数据
* Input : None
* Return : char read data 返回读出的数据(无数据也返回0)
*******************************************************************************/
char uartReadByteBLE()
{
char ret;
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9);//计算 DMA 数据尾的位置
if (rxBufferReadPos == rxBufferEnd){
// 无数据,返回
return 0;
}
ret = RxBuffer[rxBufferReadPos];
rxBufferReadPos++;
if(rxBufferReadPos >= RXBUF_SIZE){
// 超出缓冲区,回零
rxBufferReadPos = 0;
}
return ret;
}
/*******************************************************************************
* Function Name : uartAvailableBLE
* Description : get number of bytes Available to read from the UART buffer 获取缓冲区中可读数据的数量
* Input : None
* Return : uint16_t number of bytes Available to readd 返回可读数据数量
*******************************************************************************/
uint16_t uartAvailableBLE()
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel9);//计算 DMA 数据尾的位置
// 计算可读字节
if (rxBufferReadPos <= rxBufferEnd){
return rxBufferEnd - rxBufferReadPos;
}else{
return rxBufferEnd +RXBUF_SIZE -rxBufferReadPos;
}
}
char buffer[100];
void mon_log(char* format, ...)
{
va_list vArgList;
va_start(vArgList, format);
vsnprintf(buffer, 100, format, vArgList);/*把可变参数表中的数据转成字符存到buffer中,每个参数间用','隔开 */
va_end(vArgList);
}
/*******************************************************************************
* Function Name : main
* Description : Main program.
* Input : None
* Return : None
*******************************************************************************/
int main(void)
{
int32_t d;//距离
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
DMA_INIT();
USARTx_CFG(); /* USART INIT */
USART_DMACmd(UART7,USART_DMAReq_Tx|USART_DMAReq_Rx,ENABLE);
sr04_init();
lcd_init();
lcd_set_color(BLACK,WHITE);
lcd_show_string(50, 0, 32,"Yuimerlin");
lcd_set_color(BLACK,RED);
lcd_show_string(0, 32, 16,"HC-SR04 Test");
delay_ms(100);
GPIO_CFG();
// GPIO_WriteBit(GPIOA, GPIO_Pin_7,RESET); //进入 AT
GPIO_WriteBit(GPIOC, GPIO_Pin_13,SET); //enable CH9141
Delay_Ms(1000);
while(1){
Delay_Ms(400);
d = sr04_get_distance(); //获取的距离
mon_log("Distance:%d\r\n",d); // 将字符串封装进buffer
uartWriteBLEstr(buffer); //将字符串穿给蓝牙
lcd_set_color(BLACK,GREEN); //屏幕显示
lcd_show_string(30, 100, 16,"Distance : %8d", d);
// GPIO_WriteBit(GPIOA, GPIO_Pin_7,SET); // 退出AT。可用手机或电脑连接CH9141,测试数据收发
}
}
main函数前面的部分上方已介绍不再赘述,main函数部分仅为先初始化各模块,延迟,获取距离,封装进数组,发送给蓝牙并且显示于LCD屏幕上。
实物演示
先按上篇教程一样选择BLE透传参数;
演示动画
手机接收到的内容。
后话
到此蓝牙教程的中篇就结束了,希望这篇教程对您的蓝牙模板电脑端/手机端连接赤菟板的学习有所帮助。这篇例程其实是建立在远程了解冰滴咖啡的冰块融化程度的应用下诞生的,架在漏斗上由上而下检测超声波模块离冰块的距离,到临界值声明冰块已经融化,冰滴咖啡做好,这样让漏斗中的冰块在自然的高温下融化冰块,人坐在空调房里面等待,实时可以通过手机的蓝牙调试上的对话模式连接距离。详细视频可以去verimake的b站号里面的《旋转荡漾,另一种,极致慵懒的夏日冰滴咖啡》观看。
下篇教程即将讲蓝牙教程的最后一部分赤菟板与赤菟板之间通过蓝牙模块的连接以及AT指令相关。
老规矩,这次的程序和之前一样放到了gitee仓库上,文件名为:BLE_HC-SR04,当然辅助程序也在其中,文件名为:1to1_BLE-DMA
欢迎大家参考!