1. 串口基本介绍(332.36)
常用函数介绍
串口发送/接收函数:
- HAL_UART_Transmit(); 串口发送数据,使用超时管理机制
- HAL_UART_Receive(); 串口接收数据,使用超时管理机制
- HAL_UART_Transmit_IT(); 串口中断模式发送
- HAL_UART_Receive_IT(); 串口中断模式接收
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart,
uint8_t *pData, uint16_t Size, uint32_t Timeout)
作用:以阻塞的方式发送指定字节的数据
- 形参 1 :UART_HandleTypeDef 结构体类型指针变量
- 形参 2:指向要发送的数据地址
- 形参 3:要发送的数据大小,以字节为单位
- 形参 4:设置的超时时间,以ms单位
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart,
uint8_t *pData, uint16_t Size)
作用:以中断的方式接收指定字节的数据
- 形参 1 是 UART_HandleTypeDef 结构体类型指针变量
- 形参 2 是指向接收数据缓冲区
- 形参 3 是要接收的数据大小,以字节为单位
此函数执行完后将清除中断,需要再次调用以重新开启中断。
串口中断回调函数:
- HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
- HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //发送中断回调函数
- HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //接收中断回调函数
状态标记变量:
- USART_RX_STA(符合变量的命名规则下 可重新命名):
- 从 0 开始,串口中断接收到一个数据(一个字节)就自增 1。当数据读取全部 OK 时候(回车和换行符号来的时
候),那么 USART_RX_STA 的最高位置 1,表示串口数据接收全部完毕了,然后 main 函数里面可以处理数据
了。
- 从 0 开始,串口中断接收到一个数据(一个字节)就自增 1。当数据读取全部 OK 时候(回车和换行符号来的时
串口接收中断流程
2. 串口实验 (非中断)(333.37)
- 需求: 接受串口工具发送的字符串,并将其发送回串口工具。
- 硬件接线:
- TX – A10
- RX – A9
- gnd – gnd (5v 实测可以不接)
一定要记得交叉接线!!
- 串口配置:
- 选定串口
- 选择模式(异步通讯)
- 串口配置
- 使用MicroLIB库
从魔术棒打开,这个勾勾一定要打上,否则 printf 无法重映射!
- STM32CubeMx工程配置
- 串口调试助手的一些设置
- 代码(11.uart1_test/MDK-ARM)
#include <stdio.h>
#include <string.h>
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int main(void)
{
unsigned char ch[20] = {0};//char(acter)
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();//串口初始化
HAL_UART_Transmit(&huart1, (uint8_t *) "hello world\n", strlen("hello world\n"), 100);//超时100
while (1)
{
HAL_UART_Receive(&huart1, ch, 19, 100);//有\0所以要20-1
//HAL_UART_Transmit(&huart1, ch, strlen((const char *)ch), 100);
printf("%s", ch);//printf重定向(串口输出)
memset(ch, 0, strlen((const char *)ch));//清空
}
}
3. 串口实验 (中断)(334.38)
- 需求: 通过中断的方法接受串口工具发送的字符串,并将其发送回串口工具。
- 硬件接线:(同上)
- TX – A10
- RX – A9
- gnd – gnd (5v 实测可以不接)
一定要记得交叉接线!!
- 串口配置:
前4步同上
- 打开中断
- 代码(12.uart2_test/MDK-ARM)
#include <stdio.h>
//串口接收缓存(1字节)
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA = 0;
// 接收完成(一个字节)回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 判断中断是由哪个串口触发的
if(huart->Instance == USART1)
{
// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
if((UART1_RX_STA & 0x8000) == 0)//如果不是1即没有接收完成
{
// 如果已经收到了 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);
}
}
int fputc(int ch, FILE *f)//printf重定向(串口输出)
{
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,0xffff);
return ch;
}
int main()
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
HAL_UART_Receive_IT(&huart1, &buf, 1);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//判断判断串口是否接收完成
if(UART1_RX_STA & 0x8000)
{
printf("收到数据:");
// 将收到的数据发送到串口
HAL_UART_Transmit(&huart1, UART1_RX_Buffer, UART1_RX_STA & 0x3fff, 0xffff);
// 等待发送完成
while(huart1.gState != HAL_UART_STATE_READY);
printf("\r\n");
// 重新开始下一次接收
UART1_RX_STA = 0;
}
printf("Hello Jessie!\r\n");
HAL_Delay(2000);//每两秒检测一次
}
}
4. 蓝牙插座_风扇_灯(非中断)(335.39)
项目需求
- 通过蓝牙模块,实现手机控制蓝牙插座/风扇/灯。
- 本质:
- 采用蓝牙的透传功能;
- 控制 IO 口的输出。
项目框图
硬件清单
- HC01 蓝牙模块
- CH340 USB串口工具
- 杜邦线
项目设计
- HC01_TX – RX1(A10)
- HC01_RX – TX1(A9)
- gnd – gnd (5v 实测可以不接)
项目实现(串口非中断法)
- STM32CubeMx工程配置(串口非中断法)
- 第2节基础之上 设置gpio口
- 第2节基础之上 设置gpio口
- 代码(13.bluetooth_led_project1/MDK-ARM)
while (1)
{
HAL_UART_Receive(&huart1, ch, 19, 100);//有\0所以要20-1
//HAL_UART_Transmit(&huart1, ch, strlen((const char *)ch), 100);
printf("%s", ch);//printf重定向(串口输出)
if(!strcmp((char *)ch, "open"))//字符串比较 相等返回0
{
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");
}
else if (!strcmp((char *)ch, "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");
}
else
{//如果不是以上指令
if(ch[0] != '\0')
printf("指令发送错误:%s\r\n", ch);
}
memset(ch, 0, strlen((char *)ch));//清空
}
5. 蓝牙插座_风扇_灯(中断)(334.40)
- 蓝牙的波特率是9600
- 代码(14.bluetooth_led_project2/MDK-ARM)
while (1)
{
//判断判断串口是否接收完成
if(UART1_RX_STA & 0x8000)
{
printf("收到数据:");
// 将收到的数据发送到串口
HAL_UART_Transmit(&huart1, UART1_RX_Buffer, UART1_RX_STA & 0x3fff, 0xffff);
// 等待发送完成
while(huart1.gState != HAL_UART_STATE_READY);
printf("\r\n");
if(!strcmp((char *)UART1_RX_Buffer, "open"))//字符串比较 相等返回0
{
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");
}
else if (!strcmp((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");
}
else
{//如果不是以上指令
if(UART1_RX_Buffer[0] != '\0')
printf("指令发送错误:%s\r\n", UART1_RX_Buffer);
}
memset(UART1_RX_Buffer, 0, strlen((char *)UART1_RX_Buffer));//清空
// 重新开始下一次接收
UART1_RX_STA = 0;
}
//printf("Hello Jessie!\r\n");
HAL_Delay(40);
}
}
6. WiFi插座_风扇_灯(项目概述)(337.41)
项目需求
- 通过ESP8266模块,实现手机控制wifi插座/风扇/灯。
项目框图
硬件清单
- ESP8266 WIFI模块
- CH340 USB串口工具
- 杜邦线
项目设计
- 串口1用于与ESP8266通讯,串口2连接PC,用于打印log,查看系统状态。
项目实现
- 注意:
- 工作中一般不直接在中断服务函数里处理数据,而是在收到数据后直接丢给队列,再处理数据;
- 在中断服务函数里尽量减少使用延时函数及打印函数。
7. ESP工作为AP路由模式并当成服务器(338.42)
- 基于uart2_test:
- 代码(15.wifi_led_project1/MDK-ARM)(AP模式)
8. 上官二号当路由和服务器模式控制(339.43)
- STA模式
- 代码(16.wifi_led_project2/MDK-ARM)(STA模式)
9. 上官二号通过4g模块控制LED(340.44)
项目需求
- 通过4G模块,实现电脑控制插座/风扇/灯。
项目框图
- 注意: 由于硬件的限制,上官二号无法直接带动 4G 模块,可以将 4G 模块的 VCC 和 GND 插到 CH340 的 5V 和GND 里。
硬件清单
- 4G模块
- CH340
- 杜邦线
项目设计
- 服务器搭建:参照C51课程;
- 代码修改:直接复用上节课的代码,把不相关的代码删除即可
4g – USB.ttl:配置4g模块的波特率、心跳包、内网穿透ip连接
- 先建立一个服务器
- 利用花生壳软件 内网穿透
- 设置波特率(cubemx中的串口都是设置的115200 要保持一致)、设置心跳包内容和时间、让4g连接socket服务器(公网ip地址)
- 观察到 一直在接收心跳包
回到正题
- 先测试 open、close 指令是否成功(结果:成功点亮和熄灭)
- 注意:指令要换新行(shift+enter)
- 删除多余的代码
- 再次测试指令(结果:成功点亮和熄灭)