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);
    }
}
相关推荐
深圳市九鼎创展科技8 小时前
瑞芯微 RK3399 开发板 X3399 评测:高性能 ARM 平台的多面手
linux·arm开发·人工智能·单片机·嵌入式硬件·边缘计算
辰哥单片机设计8 小时前
STM32项目分享:车辆防盗报警系统
stm32·单片机·嵌入式硬件
風清掦10 小时前
【江科大STM32学习笔记-05】EXTI外部中断11
笔记·stm32·学习
小龙报10 小时前
【51单片机】从 0 到 1 玩转 51 蜂鸣器:分清有源无源,轻松驱动它奏响新年旋律
c语言·数据结构·c++·stm32·单片机·嵌入式硬件·51单片机
范纹杉想快点毕业10 小时前
嵌入式与单片机开发核心学习指南——从思维转变到第一性原理的深度实践
单片机·嵌入式硬件
Industio_触觉智能10 小时前
瑞芯微RK3566开发板规格书,详细参数配置,型号EVB3566-V1,基于RK3566核心板SOM3566邮票孔封装
嵌入式硬件·开发板·rk3568·rk3566·核心板·瑞芯微
czwxkn10 小时前
4STM32(stdl)TIM定时器
stm32·单片机·嵌入式硬件
Love Song残响10 小时前
NVIDIA显卡终极优化指南
stm32·单片机·嵌入式硬件
qq_6725927511 小时前
电源芯片为什么发热
单片机·嵌入式硬件
天天爱吃肉821811 小时前
【跨界封神|周杰伦×王传福(陶晶莹主持):音乐创作与新能源NVH测试,底层逻辑竟完全同源!(新人必看入行指南)】
python·嵌入式硬件·算法·汽车