基于stm32f103c8t6的串口中断蓝牙通讯

这一篇文章与 上一篇文章相基于 stm32f103c8t6的串口非中断蓝牙通讯上一篇文章相http://t.csdnimg.cn/7j0Ec 

相比,硬件部分是相同的。在原有的旧初上,要在stm32cube加入中断,同时代码中也要引入中断函数以及中断回调函数。到后面我谁说说我遇到的坑。

一、硬件部分

名称作用图片
stm32f103c8t6板子————
HC08蓝牙模块连接单片机通讯TX接单片机RX1,RX接单片机TX1。
usb转ttl模块模拟调试蓝牙通讯,同时在串口助手里修改蓝牙的波特率,让蓝牙的波特率和单片机相同

二、stm32cube新增加的部分

把USART1的中断打开,其余的不变

三、代码部分(黑色软件生成,蓝色自己书写)

#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#define UART1_REC_LEN 200
uint8_t buf=0;
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
uint16_t UART1_RX_STA=0;

void SystemClock_Config(void);

/* USER CODE BEGIN 0 */

unsigned char ch[20] = {0};
int fputc(int ch, FILE *f)//重映射使用
{
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,0xffff);
return ch;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//中断接收回调函数
{
// 判断中断是由哪个串口触发的
if(huart->Instance == USART1)
{
// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
   if((UART1_RX_STA & 0x8000) == 0)
  {
// 如果已经收到了 0x0d (回车),
    if(UART1_RX_STA & 0x4000)
      {
// 则接着判断是否收到 0x0a (换行)
       if(buf == 0x0a)
// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
          UART1_RX_STA |= 0x8000;
       else
// 否则认为接收错误,重新开始
        UART1_RX_STA = 0;
       }
   else // 如果没有收到了 0x0d (回车)
  {
//则先判断收到的这个字符是否是 0x0d (回车)
    if(buf == 0x0d)
   {
// 是的话则将 bit14 位置为1
     UART1_RX_STA |= 0x4000;
    }
    else
   {
// 否则将接收到的数据保存在缓存数组里
     UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
     UART1_RX_STA++;
// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
   if(UART1_RX_STA > UART1_REC_LEN - 1)
   UART1_RX_STA = 0;
  }
 }
}
// 重新开启中断
HAL_UART_Receive_IT(&huart1, &buf, 1);

}
}

/* USER CODE END 0 */

int main(void)
{
 
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, &buf, 1);
  /* USER CODE END 2 */

while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
        //判断判断串口是否接收完成
  if(UART1_RX_STA & 0x8000)
        {
            printf("收到数据:");
            if (!strcmp((const char *)UART1_RX_Buffer, "open"))
        {
             HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
            if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET)
              printf("LED1已经打开\r\n");
          
 memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer));
        }
        else if(!strcmp((const char *)UART1_RX_Buffer, "close")) 
        {
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
            if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET)
            printf("LED1已关闭\r\n");
          
 memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer));
        } 
        else 
        {
            if(UART1_RX_Buffer[0] != '\0')
            printf("指令发送错误:%s", UART1_RX_Buffer);
            
memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer));
        }
             printf("\r\n");
            // 重新开始下一次接收
             UART1_RX_STA = 0;
        }
            
            HAL_Delay(40);
    }
                
/* USER CODE END 3 */
}

四、其它补充

我不知道大家看完中断回调函数有没有啥疑问?

我来提两个问题

1.为什么在中断回调函数里还要在此调用中断接收函数?

我们打开HAL_UART_Receive_IT(&huart1, &buf, 1)的解释,可以看到下图所示

        从图中我们可以看到,当我们开启中断的时候,串口就已经READY了,第一个if满足;接着因为我们的输入非空或者大小不为0U,所以第二个if跳过。接着就来到了一个结构体指针的赋值语句,这程序的意思就是表示UART串口的接收模式为标准模式。在标准模式下,UART串口以字节为单位接收数据,当接收到一个字节后,将触发接收完成事件,并将数据保存在接收缓冲区中。所以这句话过后,就相当于中断关闭了,所以我们每次接受一个字节之后,都需要重新调用中断函数。

    还有就是要注意这里的逻辑,回调函数里是来一个字节触发一次中断,而不是把识别一个字符串触发中断,回调函数没有写循环,却可以循环。从第一个字符开始,到标志位(UART1_RX_STA=1结束,好好想想这个循。

2.我不知道有没有人关心程序中出现的变量buf,再回回调函数中,buf并没有任何人给他赋值,buf仅仅是出现在HAL_UART_Receive_IT(&huart1, &buf, 1),那么问题来了。既然没有给buf赋值,那程序中有关buf值的判断是怎么进行的呢?

比如if(buf == 0x0a)之类的语句,后来我跳转了HAL_UART_Receive_IT(&huart1, &buf, 1)解释,

我们看到串口接收变为标准模式之后,紧接着return另一个都函数里面。我们进入这个函数,大家看到了吗,这里是 huart->pRxBuffPtr一个结构体指针,而且pRxBuffPtr与pData都是指针,把指针赋值给指针,它们指向的是同一个内存位置。这意味着它们可以用来访问相同的数据,这不就意味着uart1的接收传输缓冲区与buf共享数据了嘛,所以看似buf没有赋值,但是HAL库已经帮我们赋值了。

3.memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer))

关于这句话,我建议你加上,我在手机蓝牙上试过,如果不加的话,当你输入的不是open或者close而是别的字符串,比如asdajd,它会报错,没问题。但是当你再次输入正确的指令的open或者close的时候,他还是会报错。

这是回调函数里的一个bug

    UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
                    UART1_RX_STA++;
                    
                    // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
                    if(UART1_RX_STA > UART1_REC_LEN - 1)
                        UART1_RX_STA = 0;
                }

printf("指令发送错误:%s\r\n", UART1_RX_Buffer);

个人觉得可能是printf的存在,导致只要不超过200个字节,他会一直给你累加字节

想要解决这个问题,要么你重启单片机,要么你加上memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer)),位置我已经用红色标记了。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是基于stm32f103c8t6的串口蓝牙通信代码,仅供参考: 首先,需要在GPIO中配置USART的引脚。例如,将USART1的TX引脚设置为PA9,RX引脚设置为PA10: ``` RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; // 配置USART1 TX引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1 RX引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); ``` 然后,需要在USART中配置串口参数。例如,将USART1的波特率设置为9600,数据位设置为8位,停止位设置为1位: ``` RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); ``` 接下来,需要在USART中启用中断。例如,启用USART1的接收中断: ``` NVIC_InitTypeDef NVIC_InitStructure; // 使能USART1接收中断 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 使能USART1接收中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); ``` 然后,在USART1的中断服务函数中,可以接收蓝牙模块发送的数据。例如,接收一个字节: ``` void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { uint8_t byte = USART_ReceiveData(USART1); // 处理接收到的数据 } } ``` 最后,可以通过USART1的发送函数向串口发送数据。例如,向蓝牙模块发送一个字节: ``` USART_SendData(USART1, byte); ``` 以上是基于stm32f103c8t6的串口蓝牙通信代码,仅供参考。实际使用时,还需要根据具体的蓝牙模块和需求进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值