这一篇文章与 上一篇文章相基于 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)),位置我已经用红色标记了。



















