STM32之串口(二)

1. 蓝牙模块

1.1 简介

• HC-08 蓝牙模块是通过串口与单片机 进行通信,这个模块既可以作为主机也可以作为从机(通过 AT 指令配置),并且最远传输距离为80m,最大传输速度为1Mbps。如图:

1.2 引脚

• STATE:状态输出引脚。未连接时,则为低电平。连接成功时,则为高电平。可以在程序中作指示引脚使用。

• RXD:串口接收引脚。接单片机的 TX 引脚。

• TXD:串口发送引脚。接单片机的 RX 引脚。

• GND:接地电源。

• VCC:输入 3.3~6V 的电源。

• KEY:主机用于清除配对的从机地址记忆(需要拉高电平 200ms 以上)。

• 但是正常情况下只需要接 RXD,TXD,VCC,DND这四个引脚。

• 蓝牙模块上还有一个 LED灯和一个小按键 (按键控制着引脚 KEY )。默认情况下,当 LED灯闪烁时表示蓝牙模块当前为从机,正在等待连接。而长亮的时候就代表已经有主机连接上该模块,可以正常进行透传通讯了。

• 当按键按下后,主机将清除已被记录的从机地址。另外,也可使用 AT+CLEAR 指令,实现主机清除已记录的从机地址的功能。

1.3 常用AT指令

• 测试的,发AT,返回OK,代表模块正常,返回OK。

• AT+RESET是重启模块指令。

• AT+DEFAULT是恢复出场指令。

• AT+BAUD=xx,y是修改波特率指令,一般来说默认波特率是9600。

1.4 实操

• 在bluetooth.c中

cpp 复制代码
#include "bluetooth.h"
#include "stdio.h"
#include "string.h"
#include "stdarg.h"
UART_HandleTypeDef uart2_handle = {0};//串口的句柄
uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];//定义接收的数据存放位置
uint16_t uart2_rx_len = 0;//计数器,表示接收了多少个数


//串口初始化
void bt_init(uint32_t baudrate){
    uart2_handle.Instance = USART2;//选择串口2
    uart2_handle.Init.BaudRate = baudrate;
    uart2_handle.Init.Mode = UART_MODE_TX_RX;
    uart2_handle.Init.Parity = UART_PARITY_NONE;//不打开检验位
    uart2_handle.Init.StopBits = UART_STOPBITS_1;//1个停止位
    uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;//传输字长为8位
    uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流不打开
    HAL_UART_Init(&uart2_handle);
}

void uart2_clear(){
    memset(uart2_rx_buf,0,sizeof(uart2_rx_buf));
    uart2_rx_len = 0;
}


void USART2_IRQHandler(){
    uint8_t receive_data = 0;
    if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_RXNE) != RESET){//看是不是被置1了
        if(uart2_rx_len >= sizeof(uart2_rx_buf))
            uart2_rx_len = 0;//接收前需要判断一下,,uart2_cnt是否超出总长度uart2_RX_BUF_SIZE
        HAL_UART_Receive(&uart2_handle,&receive_data,1,1000);//接收数据
        uart2_rx_buf[uart2_rx_len++] = receive_data;//把接收到的数据放进数组
        
        //HAL_UART_Transmit(&uart2_handle,&receive_data,1,1000);
    }
    if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_IDLE) != RESET){
        __HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);
        printf("bt recv: %s\r\n",uart2_rx_buf);
        uart2_clear();//在这里,已经把buf清空了
    }
    
}

//void send_buf(char* buf,uint8_t size){
//    
//    
//    HAL_UART_Transmit(&uart2_handle,(uint8_t *)buf,size,100);
//}

void bt_send(char * format, ...)
{
    uint8_t send_buf[128] ={0};
    va_list arg;//可变的参数列表
    va_start(arg, format);//初始化arg,使其指向可变参数列表的起始位置,format是传递给函数的格式字符串参数,va_start告诉编译器从format之后的参数开始处理可变参数。
    vsprintf((char *)send_buf, format, arg);//函数将可变参数格式化为字符串,并存储在send_buf中。
    va_end(arg);//结束处理
    HAL_UART_Transmit(&uart2_handle, send_buf, sizeof(send_buf), 100);//数据发给手机,
}

• uart1.c

cpp 复制代码
#include "uart1.h"
#include "stdio.h"
#include "string.h"
UART_HandleTypeDef uart1_handle = {0};//串口的句柄
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];//定义接收的数据存放位置
uint16_t uart1_rx_len = 0;//计数器,表示接收了多少个数


//串口初始化
void uart1_init(uint32_t baudrate){
    uart1_handle.Instance = USART1;//选择串口1
    uart1_handle.Init.BaudRate = baudrate;
    uart1_handle.Init.Mode = UART_MODE_TX_RX;
    uart1_handle.Init.Parity = UART_PARITY_NONE;//不打开检验位
    uart1_handle.Init.StopBits = UART_STOPBITS_1;//1个停止位
    uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;//传输字长为8位
    uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流不打开
    HAL_UART_Init(&uart1_handle);
}


void HAL_UART_MspInit(UART_HandleTypeDef *huart){
    if(huart->Instance == USART1){
        GPIO_InitTypeDef gpio_init = {0};
        __HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIO时钟
        __HAL_RCC_USART1_CLK_ENABLE();
        gpio_init.Mode = GPIO_MODE_AF_PP;//TX线是看GPIO外设配置为复用推挽
        gpio_init.Pin = GPIO_PIN_9;
        gpio_init.Pull = GPIO_PULLUP;//默认上拉
        gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
        
        gpio_init.Mode = GPIO_MODE_INPUT;//RX线是看GPIO外设配置为上拉输入
        gpio_init.Pin = GPIO_PIN_10;
        HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
        
        HAL_NVIC_SetPriority(USART1_IRQn,3,3);
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);//使能RXNE中断
        __HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);//使能空闲中断
    }else if(huart->Instance == USART2){//蓝牙的
        GPIO_InitTypeDef gpio_init = {0};
        __HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIO时钟
        __HAL_RCC_USART2_CLK_ENABLE();
        gpio_init.Mode = GPIO_MODE_AF_PP;//TX线是看GPIO外设配置为复用推挽
        gpio_init.Pin = GPIO_PIN_2;
        gpio_init.Pull = GPIO_PULLUP;//默认上拉
        gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
        
        gpio_init.Mode = GPIO_MODE_INPUT;//RX线是看GPIO外设配置为上拉输入
        gpio_init.Pin = GPIO_PIN_3;
        HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
        
        HAL_NVIC_SetPriority(USART2_IRQn,3,3);
        HAL_NVIC_EnableIRQ(USART2_IRQn);
        __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);//使能RXNE中断
        __HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);//使能空闲中断
    }
}
int fputc(int ch,FILE *f){//重定向printf函数
    while((USART1->SR & 0x40) == 0);//一直等 发送数据寄存器不为空 
    USART1->DR = (uint8_t)ch;
    return ch;
}



void uart1_clear(){
    memset(uart1_rx_buf,0,sizeof(uart1_rx_buf));
    uart1_rx_len = 0;
}


void USART1_IRQHandler(){
    uint8_t receive_data = 0;
    if(__HAL_UART_GET_FLAG(&uart1_handle,UART_FLAG_RXNE) != RESET){//看是不是被置1了
        if(uart1_rx_len >= sizeof(uart1_rx_buf))
            uart1_rx_len = 0;//接收前需要判断一下,,uart1_cnt是否超出总长度UART1_RX_BUF_SIZE
        HAL_UART_Receive(&uart1_handle,&receive_data,1,1000);//接收数据
        uart1_rx_buf[uart1_rx_len++] = receive_data;//把接收到的数据放进数组
        
        //HAL_UART_Transmit(&uart1_handle,&receive_data,1,1000);
    }
    if(__HAL_UART_GET_FLAG(&uart1_handle,UART_FLAG_IDLE) != RESET){
        __HAL_UART_CLEAR_IDLEFLAG(&uart1_handle);
        printf("recv: %s\r\n",uart1_rx_buf);
        uart1_clear();//在这里,已经把buf清空了
    }
    
}

• main.c

cpp 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "bluetooth.h"


int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    led_init();                         /* LED初始化 */
    uart1_init(115200);
    bt_init(115200);
    uint8_t i = 0;
    while(1)//流水灯实验
    { 
        bt_send("hello bt%d\r\n",i++);
        delay_ms(500);
    }
}

1.5 实战2,用蓝牙控制插座

• bluetooth.c

cpp 复制代码
#include "bluetooth.h"
#include "stdio.h"
#include "string.h"
#include "stdarg.h"
#include "plugin.h"
UART_HandleTypeDef uart2_handle = {0};//串口的句柄
uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];//定义接收的数据存放位置
uint16_t uart2_rx_len = 0;//计数器,表示接收了多少个数


//串口初始化
void bt_init(uint32_t baudrate){
    uart2_handle.Instance = USART2;//选择串口2
    uart2_handle.Init.BaudRate = baudrate;
    uart2_handle.Init.Mode = UART_MODE_TX_RX;
    uart2_handle.Init.Parity = UART_PARITY_NONE;//不打开检验位
    uart2_handle.Init.StopBits = UART_STOPBITS_1;//1个停止位
    uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;//传输字长为8位
    uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流不打开
    HAL_UART_Init(&uart2_handle);
}

void uart2_clear(){
    memset(uart2_rx_buf,0,sizeof(uart2_rx_buf));
    uart2_rx_len = 0;
}

void USART2_IRQHandler(){
    uint8_t receive_data = 0;
    if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_RXNE) != RESET){//看是不是被置1了
        if(uart2_rx_len >= sizeof(uart2_rx_buf))
            uart2_rx_len = 0;//接收前需要判断一下,,uart2_cnt是否超出总长度uart2_RX_BUF_SIZE
        HAL_UART_Receive(&uart2_handle,&receive_data,1,1000);//接收数据
        uart2_rx_buf[uart2_rx_len++] = receive_data;//把接收到的数据放进数组
        
        //HAL_UART_Transmit(&uart2_handle,&receive_data,1,1000);
    }
    if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_IDLE) != RESET){
        __HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);
        printf("bt recv: %s\r\n",uart2_rx_buf);
        if(strstr((char *)uart2_rx_buf,"on") != NULL){
            plugin_on();
        }else if(strstr((char *)uart2_rx_buf,"off") != NULL){
            plugin_off();
        }
        
        uart2_clear();//在这里,已经把buf清空了
    }
    
}

//void send_buf(char* buf,uint8_t size){
//    
//    
//    HAL_UART_Transmit(&uart2_handle,(uint8_t *)buf,size,100);
//}

void bt_send(char * format, ...)
{
    uint8_t send_buf[128] ={0};
    va_list arg;
    va_start(arg, format);
    vsprintf((char *)send_buf, format, arg);
    va_end(arg);
    HAL_UART_Transmit(&uart2_handle, send_buf, sizeof(send_buf), 100);
}

• 在uart.c同上面第一实操的是一样的。

• plugin.c

cpp 复制代码
#include "plugin.h"


void plugin_init(){//继电器可以看作成一个开关,开关连接着负载,这里的负载是个喇叭
    GPIO_InitTypeDef gpio_init = {0};
    __HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIO时钟
    gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init.Pin = GPIO_PIN_6;//推挽输出
    gpio_init.Pull = GPIO_PULLUP;//默认上拉
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB,&gpio_init);//初始化gpio
    plugin_off();//关灯

}

void plugin_on(){
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
}

void plugin_off(){
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}

uint8_t get_plugin_staut(){
    return (uint8_t) HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6);
}

• main.c

cpp 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "bluetooth.h"
#include "plugin.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    led_init();                         /* LED初始化 */
    uart1_init(115200);
    bt_init(115200);
    plugin_init();
    uint8_t i = 0;
    while(1)//流水灯实验
    { 
        bt_send("hello bt%d\r\n",i++);
        delay_ms(500);
    }
}

2. 4G模块

2.1 内网穿透

• 内网穿透的原理就像在内网和外网之间搭建了一座桥梁,使得外部网络可以穿过内网的障碍,直接访问内部的设备。如图:

• 内网里面的pc可以通过用网络调试助手搭建一个服务器,然后用花生壳配置生成一个可以给外界设备使用的公网地址。

• 然后可以在EC05配置4g模块,将地址和端口传进去。

2.2 4g模块,如图:

2.3 4g模块指示灯作用,如图:

• 注意:DATA和LINK反了,绿色应该时连接服务器,橙色应该时 串口接收/发送数据。

2.4 实操,4g控制led

• led.c

cpp 复制代码
#include "led.h"

void led_init(){
    GPIO_InitTypeDef gpio_init = {0};
    __HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIO时钟
    gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init.Pin = GPIO_PIN_8 | GPIO_PIN_9;//推挽输出
    gpio_init.Pull = GPIO_PULLUP;//默认上拉
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB,&gpio_init);//初始化gpio
    led1_off();//关灯
    led2_off();

}

void led1_on(){
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}

void led1_off(){
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}


void led1_toggle(){
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}


void led2_on(){
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
}

void led2_off(){
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
}


void led2_toggle(){
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}

• e840.c

cpp 复制代码
#include "e840.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"

UART_HandleTypeDef e840_handle = {0};//串口的句柄
uint8_t e840_rx_buf[E840_RX_BUF_SIZE];//定义接收的数据存放位置
uint16_t e840_cnt = 0;//计数器,表示接收了多少个数
uint16_t e840_cntPre = 0;//保存接收前一个数是第几个数



//串口初始化
void e840_uart_init(uint32_t baudrate){
    e840_handle.Instance = USART2;//选择串口2
    e840_handle.Init.BaudRate = baudrate;
    e840_handle.Init.Mode = UART_MODE_TX_RX;
    e840_handle.Init.Parity = UART_PARITY_NONE;//不打开检验位
    e840_handle.Init.StopBits = UART_STOPBITS_1;//1个停止位
    e840_handle.Init.WordLength = UART_WORDLENGTH_8B;//传输字长为8位
    e840_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流不打开
    HAL_UART_Init(&e840_handle);
}

uint8_t e840_wait_receive(){
    if(e840_cnt == 0)
        return E840_ERROR;
    
    if(e840_cnt == e840_cntPre){
        e840_cnt = 0;
        return E840_EOK;
    }
    e840_cntPre = e840_cnt;
    return E840_ERROR;
    

}

void e840_clear(){
    memset(e840_rx_buf,0,sizeof(e840_rx_buf));
    e840_cnt = 0;
}
uint16_t e840_reveice_data(char * recv_data){
    if(e840_wait_receive() == E840_EOK){
        printf("e840 recv: %s\r\n",e840_rx_buf);
        memcpy(recv_data,e840_rx_buf,strlen((char *)e840_rx_buf));//把接收区的数据拷贝出去
        e840_clear();
        return strlen(recv_data);
    }
    return 0;
    
}



void e840_init(uint32_t baudrate){
    e840_uart_init(baudrate);
}
void USART2_IRQHandler(){
    uint8_t receive_data = 0;
    if(__HAL_UART_GET_FLAG(&e840_handle,UART_FLAG_RXNE) != RESET){//看是不是被置1了
        if(e840_cnt >= sizeof(e840_rx_buf))
            e840_cnt = 0;//接收前需要判断一下,,e840_cnt是否超出总长度e840_RX_BUF_SIZE
        HAL_UART_Receive(&e840_handle,&receive_data,1,1000);//接收数据
        e840_rx_buf[e840_cnt++] = receive_data;//把接收到的数据放进数组
      
        //E840_clear();
        //HAL_UART_Transmit(&E840_handle,&receive_data,1,1000);
    }
}

• main.c

cpp 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "e840.h"

#include "string.h"
int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    led_init();                         /* LED初始化 */
    uart1_init(115200);
    e840_init(115200);

    printf("hello world\r\n");
    char recv_data[E840_RX_BUF_SIZE];
    while(1)
    { 
        e840_reveice_data(recv_data);
        if(strstr(recv_data,"ON") != NULL)
            led1_on();
        else if(strstr(recv_data,"OFF") != NULL)
            led1_off();
        delay_ms(10);
    }
}
相关推荐
夜月yeyue2 小时前
VFS (虚拟文件系统) 核心架构
linux·c++·单片机·嵌入式硬件·架构
Y1rong2 小时前
STM32之串口(一)
网络·stm32·嵌入式硬件
想睡觉的树2 小时前
解决keil5编译慢的问题-亲测有效-飞一般的感觉
c语言·stm32·嵌入式硬件
__万波__2 小时前
STM32L475串口打印改为阻塞式打印兼DMA, 两种打印方式实时切换
stm32·单片机·嵌入式硬件
猫猫的小茶馆2 小时前
【Linux 驱动开发】二. linux内核模块
linux·汇编·arm开发·驱动开发·stm32·嵌入式硬件·架构
飞睿科技2 小时前
解析ESP-SparkBot开源大模型AI桌面机器人的ESP32-S3核心方案
人工智能·嵌入式硬件·物联网·机器人·esp32·乐鑫科技·ai交互
風清掦2 小时前
【江科大STM32学习笔记-03】GPIO通用输入输出口
笔记·stm32·单片机·学习
碎碎思2 小时前
走向开放硅:Baochip-1x 的 RISC-V MCU 架构与工程实践
单片机·嵌入式硬件·risc-v
搞全栈小苏2 小时前
嵌入式之 LVGL 的切换页面研究:杜绝内存泄漏(单片机与 Linux 平台)(链表与多进程方式)
linux·单片机·链表·lvgl