led模块功能封装
#include "led.h"
#include "sys.h"
//初始化GPIO函数
void led_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_initstruct.Pull = GPIO_PULLUP;//上拉
gpio_initstruct.Speed= GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&gpio_initstruct);
//关闭LED
led1_off();
led3_off();
}
//点亮LED的函数
void led1_on(void)
{
//由电路图可知,将GPIO拉低
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
//熄灭LED1的函数
void led1_off(void)
{
//由电路图可知,将GPIO拉低
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
//翻转LED1状态的函数
void led1_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
void led3_on(void)
{
//由电路图可知,将GPIO拉低
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
}
//熄灭LED1的函数
void led3_off(void)
{
//由电路图可知,将GPIO拉低
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
}
//翻转LED1状态的函数
void led3_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}
4G模块功能代码及指令
内网穿透
1. 内网穿透
内网穿透的原理就像在内网和外网之间搭建了一座桥梁,使得外部网络可以穿过内网的障碍,直接访问内部 的设备。
2. 花生壳配置
案例演示
右边是通过官网的方式来连接的
4G模块
上位机配置: 1. 选择端口号(根据自己的端口选择) 2. 选择波特率(默认115200) 3. 打开串口 4. 点击进入配置,上位机会自动发送AT指令进行配置 5. 读取参数,此命令可以读取4g模块的详细信息,并打印带基本信息栏(这一步很重要,否则在第九步的 时候会出现问题) 6. 选择链路1 7. 默认是连接亿佰特的服务器 这边我们需要修改成我们自己的,具体从花生壳获取(是一串域名,或者 IP地址,他都支持,根据自己获取到的填写就可以) 8. 输入从花生壳获取的端口号 9. 点击保存配置(对应第五步,我们很多的配置需要获取到模块默认配置,保存时才会不改变) 10. 点击重启(重启后默认进入透传模式,无需在做其他操作,直接链接到串口助手上即可发送与接收信 息)
不用自己敲指令,有专门的上位机
服务端
进入AT模式后在链接2中,复制映射IP时不能复制端口,否则,外网与内容能连接但是不能通讯通过接口
重启之后就默认进入透传模式
TCPC代表端口
填入的是映射出来的地址
4G模块可以左边能接收到,但是右边收不到
网络调试助手的TCP client打不开
解决问题1
联系快速的断连
有时候是连接的但是确实断的
解决方法
网络调试2 助手在协议类型为TCP Client打不开的情况则,需连接WiFi模块的wifi使其处在同一网段下,如果电脑连接路由器家庭WiFi则与服务器不是同一网段。
就会出现连接不了的情况
功能代码
头文件
#include "sys.h"
#include "e840.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"
/* UART收发缓冲大小 */
#define E840_RX_BUF_SIZE 128
#define E840_TX_BUF_SIZE 64
/* 错误代码 */
#define E840_EOK 0 /* 没有错误 */
#define E840_ERROR 1 /* 通用错误 */
#define E840_ETIMEOUT 2 /* 超时错误 */
#define E840_EINVAL 3 /* 参数错误 */
void e840_init(uint32_t baudrate);//所以增加新添加一个
uint16_t e840_receive_data(char *recv_data);
void e840_test(void);
uint8_t e840_tx_buf[E840_TX_BUF_SIZE];
uint8_t e840_rx_buf[E840_RX_BUF_SIZE];
uint16_t e840_cnt = 0 ,e840_cntPre = 0; /* es840接收缓冲区 */
UART_HandleTypeDef e840_handle; /* esp8266句柄 */
#include "sys.h"
#include "e840.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"
uint8_t e840_tx_buf[E840_TX_BUF_SIZE];
uint8_t e840_rx_buf[E840_RX_BUF_SIZE];
uint16_t e840_cnt = 0 ,e840_cntPre = 0; /* es840接收缓冲区 */
UART_HandleTypeDef e840_handle; /* esp8266句柄 */
/**
* @brief 串口1初始化函数
* @param baudrate: 波特率, 根据自己需要设置波特率值
* @retval 无
*/
void e840_uart_init(uint32_t baudrate)//正常会有启动的流程只是作为一个串口的初始化
{
/*esp8266 初始化设置*/
e840_handle.Instance = USART2; /* USART1 */
e840_handle.Init.BaudRate = baudrate; /* 波特率 */
e840_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
e840_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
e840_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
e840_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
e840_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
HAL_UART_Init(&e840_handle); /* HAL_UART_Init()会使能e840 */
}
/**
* @brief esp8266接收缓冲区清除
* @param 无
* @retval 无
*/
void e840_rx_clear(void)
{
memset(e840_rx_buf, 0, sizeof(e840_rx_buf)); /* 清空接收缓冲区 */
e840_cnt = 0; /* 接收计数器清零 */
}
/**
* @brief 串口1中断服务函数
* @note 在此使用接收中断及空闲中断,实现不定长数据收发
* @param 无
* @retval 无
*/
void USART2_IRQHandler(void)
{
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&e840_handle, UART_FLAG_RXNE) != RESET){ /* 获取接收RXNE标志位是否被置位 */
if(e840_cnt >= sizeof(e840_rx_buf)) /* 如果接收的字符数大于接收缓冲区大小, */
e840_cnt = 0; /* 则将接收计数器清零 */
HAL_UART_Receive(&e840_handle, &receive_data, 1, 1000); /* 接收一个字符 */
e840_rx_buf[e840_cnt++] = receive_data; /* 将接收到的字符保存在接收缓冲区 */
}
if (__HAL_UART_GET_FLAG(&e840_handle, UART_FLAG_IDLE) != RESET) /* 获取接收空闲中断标志位是否被置位 */
{
printf("recv: %s\r\n", e840_rx_buf); /* 将接收到的数据打印出来 */
e840_rx_clear();
__HAL_UART_CLEAR_IDLEFLAG(&e840_handle); /* 清除UART总线空闲中断 */
}
}
uint8_t e840_wait_receive(void)
{
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;
}
uint8_t e840_send_command(char *cmd,char *res)//发送一个指令去 在指定时间内返回回来 否则没有接收到一个返回值
{
uint8_t timeout_out = 250;
e840_rx_clear(); //防止忍心内的数据影响
HAL_UART_Transmit(&e840_handle,(uint8_t *)cmd,strlen(cmd),100);//发送命令 cmd在这里是一个uint8_t的数据 长度 阻塞的值
//等待
while(timeout_out--)//不停的等待
{
if(e840_wait_receive() == E840_EOK)
{
if(strstr((const char*)e840_rx_buf, res) != NULL)//判断是否又有期待的字符串
return E840_EOK;
}
delay_ms(10);
}
return E840_ERROR;//否则接收错误
}
//只是内部处理外部是不知道的用不了,为了便于外部能获取RX_BUF接收缓冲区内容接收到多少的数据,可以将接收到的字符,对代码进行修改
//复制一份给TX_BUF
//接收的函数通返回值传出来,这样RX_bUF就能传出来 memcpy(recv_data,)
uint16_t e840_receive_data(char *recv_data)
{
if(e840_wait_receive() == E840_EOK)
{
printf("esp8266 recv: %s\r\n", e840_rx_buf);
memcpy(recv_data, e840_rx_buf,strlen((const char *)e840_rx_buf));
e840_rx_clear(); //清空之前一样的,清空为下一次数据做准备,用recv_data作为返回值
return strlen((const char *)recv_data);
}
return 0;//没有接收到数据
}
//发送的函数 不定长数据 参数不固定
void e840_send_data(char *fmt, ...)
{
va_list ap;
uint16_t len;
va_start(ap,fmt);
vsprintf((char *)e840_tx_buf,fmt, ap);
va_end(ap);
len = strlen((const char*)e840_tx_buf);
HAL_UART_Transmit(&e840_handle, e840_tx_buf, len,100);//发送
}
void e840_init(uint32_t baudrate)//所以增加新添加一个
{
e840_uart_init(baudrate);
}
//测试函数
main函数代码
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.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("helloworld!\r\n");
char recv_data[E840_RX_BUF_SIZE];//接收传入参数
while(1)
{
e840_receive_data(recv_data);//接收到数据tcp服务器传输过来的字符串
if(strstr(recv_data, "ON") != NULL)//判断字符串有ON,打开风扇
led1_on();
else if(strstr(recv_data, "OFF") != NULL)
led1_off();
delay_ms(10);
}
}