赤兔板CH32V307基于wifi模块ESP8266的小时钟
前言
新的学习项目又来啦!此次带来的是CH32V307赤菟板的外接的wifi模块ESP8266的相关教学。这次的教学将从AT指令,再到后面的时钟教学。老规矩,基础薄弱的同学可以移步论坛的赤菟教程目录:传送门,本篇会用到板子上的外接端口,ESP8266,LCD屏幕。
ESP8266简介
ESP8266 Wi-Fi模块作为可以进行WiFi传输的模块集成了业内领先的Tensilica L106超低功耗32位微型MCU,带有16位精简模式,主频可达160MHz。同时支持RTOS(Real Time Operating System)集成Wi-Fi MAC/BB/RF/PA/LNA和 IEEE802.11 b/g/n协议,支持完整的TCP/IP协议栈,也支持STA(Station),AP(Access Point),STA+AP三种模式。
由于CH32V307的wifi模块并不是直接封装进去的,但是有留有他的专用外接口,将ESP8266按上图的方向插入接口即可开始使用。
端口功能 | 引脚功能 | 功能 | 备注 |
WiFi接口 | PC0 | UART6_TX | ESP8266_RX <兼容 ESP-01,ESP-01S WiFi 模块> |
| PC1 | UART6_RX | ESP8266_TX <使用时注意 WiFi 天线朝向板外> |
AT指令
一般来说,ESP8266都是通过AT指令进行烧录,AT指令我们上次就在蓝牙教程里面提到过,之前只是提到的,这次的模块沟通全靠AT指令,这里详细说一下:AT指令是应用于终端设备与PC应用之间的连接与通信的指令。AT即Attention。每个AT命令行中只能包含一条AT指令;对于AT指令的发送,除AT两个字符外,最多可以接收1056个字符的长度(包括最后的空字符)。
上期教程:传送门
指令集
这里仅列出接下来例程中出现的指令,具体详细的指令集还请看数据手册。
指令 | 说明 |
AT | 测试AT启动 |
ATE | 开启或关闭AT回显功能 |
AT+CWMODE | 查询/设置WiFi模式(Station/SoftAP/Station+SoftAP) |
AT+RST | 重启模块 |
AT+CIPMUX | 启用/禁用多连接模式 |
AT+CWJAP | 连接AP(无线访问节点) |
AT+CIPSTART | 建立TCP连接、UDP传输或SSL连接 |
AT+CIPMODE | 查询/设置传输模式 |
AT+CIPSEND | 在普通传输模式或WiFi透传模式下发送数据 |
[仅适用透传模式] +++ | 退出透传模式 |
AT+CIPCLOSE | 关闭TCP/UDP/SSL连接 |
AT指令测试
这边测试使用的程序基本上和之前蓝牙教程一样的,仅将蓝牙串口UART7变为WiFi串口UART6即可。
用WCHLink串口连接电脑,烧录好辅助程序之后,选择你确定的串口(我这边是COM5),波特率设置为115200,其他为默认,打开串口,按下RST看到上图的数据,说明烧录正确(如果没有显示请查看你的跳线帽连接的是UART1还是UART2,接UART1才能看到),将跳线帽换到UART2,测试开始。
- AT:判断WiFi模块是否存在以及是否支持AT指令;
- ATE1:开启AT回显,仅为方便演示,默认即为开启,ATE0可关;
- AT+CWMODE=1:设置WiFi模块为STA模式(2为AP模式,3为STA+AP模式);
- AT+RST:设置完模块模式之后需要reset使其生效,为改变模式可以不用复位;
- AT+CIPMUX=0:设置为单连接模式(1为多连接模式);
- AT+CWJAP="SSID","PASSWORD":连接热点,对应的是热点名和热点密码,注意,务必等待该条指令返回WIFI GOT IP后再发送下面的指令;
- AT+CIPSTART="TCP","api.k780.com",80:建立TCP连接;
- AT+CIPMODE=1:进入透传模式;
- AT+CIPSEND:AT+CIPMODE=1并且作为客户端模式下,进入透传模式(需要支持硬件流控,否则大量数据情况下会丢数据)模块收到指令后先换行返回”>”,然后会发送串口接收到的数据。
- 发送获取时间信息的网址,记得前面加GET,不可少。
GET /?app=life.time&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json HTTP/1.1
Host: api.k780.com:88
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0
注意,需要包含上面两次换行!
不加GET即得到下图反馈,给的时间是GMT0的,慢8h。
- +++:不需要换行也不要空格,退出透传模式(此句不发,发除10以外的都反馈反馈上图的内容,AT指令将无法发送);
- AT+CIPCLOSE:退出TCP连接,想要重新进入需从7开始。
必要准备
当完成AT指令之后基本上就剩逻辑的编写,连接各模块。
LCD屏幕显示部分
之前的教程有详细说明,此处不再赘述,提一个我遇到的问题:lcd.c中有使用printf函数,该函数需要提前初始化UART1,如果想单独使用可以注释掉那一行,如果不初始化或者注释掉,程序会在这里进入死循环。
void lcd_init(void)
{
u16 id=0;
lcd_gpio_init();
lcd_fsmc_init();
lcd_write_cmd(0X04);
id = lcd_read_data(); //dummy read
id = lcd_read_data(); //读到0X85
id = lcd_read_data(); //读取0X85
id <<= 8;
id |= lcd_read_data(); //读取0X52
printf("id = %x\r\n",id);
delay_ms(50);
/* Memory Data Access Control */
lcd_write_cmd(0x36);
lcd_write_data(0x00);
/* RGB 5-6-5-bit */
lcd_write_cmd(0x3A);
lcd_write_data(0x05);
/* Porch Setting */
lcd_write_cmd(0xB2);
lcd_write_data(0x0C);
lcd_write_data(0x0C);
lcd_write_data(0x00);
lcd_write_data(0x33);
lcd_write_data(0x33);
/* Gate Control */
lcd_write_cmd(0xB7);
lcd_write_data(0x00);
/* VCOM Setting */
lcd_write_cmd(0xBB);
lcd_write_data(0x3F);
/* LCM Control */
lcd_write_cmd(0xC0);
lcd_write_data(0x2C);
/* VDV and VRH Command Enable */
lcd_write_cmd(0xC2);
lcd_write_data(0x01);
/* VRH Set */
lcd_write_cmd(0xC3);
lcd_write_data(0x0D);
/* VDV Set */
lcd_write_cmd(0xC4);
lcd_write_data(0x20);
/* Frame Rate Control in Normal Mode */
lcd_write_cmd(0xC6);
lcd_write_data(0x03); //60Hz-0x0F 82Hz-0x07 99Hz-0x03
/* Power Control 1 */
lcd_write_cmd(0xD0);
lcd_write_data(0xA4);
lcd_write_data(0xA1);
/* Positive Voltage Gamma Control */
lcd_write_cmd(0xE0);
lcd_write_data(0xF0);
lcd_write_data(0x03);
lcd_write_data(0x09);
lcd_write_data(0x03);
lcd_write_data(0x03);
lcd_write_data(0x10);
lcd_write_data(0x2D);
lcd_write_data(0x43);
lcd_write_data(0x3F);
lcd_write_data(0x33);
lcd_write_data(0x0D);
lcd_write_data(0x0E);
lcd_write_data(0x29);
lcd_write_data(0x32);
/* Negative Voltage Gamma Control */
lcd_write_cmd(0xE1);
lcd_write_data(0xF0);
lcd_write_data(0x0C);
lcd_write_data(0x10);
lcd_write_data(0x0E);
lcd_write_data(0x0E);
lcd_write_data(0x0A);
lcd_write_data(0x2D);
lcd_write_data(0x33);
lcd_write_data(0x45);
lcd_write_data(0x3A);
lcd_write_data(0x14);
lcd_write_data(0x19);
lcd_write_data(0x31);
lcd_write_data(0x37);
/* Display Inversion On */
lcd_write_cmd(0x21);
/* Sleep Out */
lcd_write_cmd(0x11);
/* wait for power stability */
delay_ms(100);
lcd_clear(BLACK);
/* display on */
GPIO_SetBits(GPIOB,GPIO_Pin_14);
lcd_write_cmd(0x29);
}
WiFi模块读取和写入部分
/* Global define */
#define RXBUF_SIZE 1024 // DMA buffer size
#define size(a) (sizeof(a) / sizeof(*(a)))
/* Global Variable */
u8 TxBuffer[] = " ";
u8 RxBuffer[RXBUF_SIZE]={0};
/*******************************************************************************
* Function Name : USARTx_CFG
* Description : Initializes the USART6 peripheral.
* 描述 : 串口初始化
* Input : None
* Return : None
*******************************************************************************/
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART6, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* USART6 TX-->C0 RX-->C1 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //RX,输入上拉
GPIO_Init(GPIOC, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200; // 波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 数据位 8
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位 1
USART_InitStructure.USART_Parity = USART_Parity_No; // 无校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //使能 RX 和 TX
USART_Init(UART6, &USART_InitStructure);
DMA_Cmd(DMA2_Channel7, ENABLE); //开启接收 DMA
USART_Cmd(UART6, ENABLE); //开启UART
}
/*******************************************************************************
* Function Name : DMA_INIT
* Description : Configures the DMA.
* 描述 : DMA 初始化
* Input : None
* Return : None
*******************************************************************************/
void DMA_INIT(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
// TX DMA 初始化
DMA_DeInit(DMA2_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&UART6->DATAR); // DMA 外设基址,需指向对应的外设
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)TxBuffer; // DMA 内存基址,指向发送缓冲区的首地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 方向 : 外设 作为 终点,即 内存 -> 外设
DMA_InitStructure.DMA_BufferSize = 0; // 缓冲区大小,即要DMA发送的数据长度,目前没有数据可发
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址自增,禁用
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增,启用
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据位宽,8位(Byte)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据位宽,8位(Byte)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 普通模式,发完结束,不循环发送
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // 优先级最高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2P,禁用M2M
DMA_Init(DMA2_Channel6, &DMA_InitStructure);
// RX DMA 初始化,环形缓冲区自动接收
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer; // 接收缓冲区
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向 : 外设 作为 源,即 内存 <- 外设
DMA_InitStructure.DMA_BufferSize = RXBUF_SIZE; // 缓冲区长度为 RXBUF_SIZE
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式,构成环形缓冲区
DMA_Init(DMA2_Channel7, &DMA_InitStructure);
}
/*******************************************************************************
* Function Name : uartWriteWiFi
* Description : send data to ESP8266 via UART6
* 描述 : 向 WiFi 模组发送数据
* Input : char * data data to send 要发送的数据的首地址
* uint16_t num number of data 数据长度
* Return : RESET UART6 busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteWiFi(char * data , uint16_t num)
{
//如上次发送未完成,返回
if(DMA_GetCurrDataCounter(DMA2_Channel6) != 0){
return RESET;
}
DMA_ClearFlag(DMA2_FLAG_TC8);
DMA_Cmd(DMA2_Channel6, DISABLE ); // 关 DMA 后操作
DMA2_Channel6->MADDR = (uint32_t)data; // 发送缓冲区为 data
DMA_SetCurrDataCounter(DMA2_Channel6,num); // 设置缓冲区长度
DMA_Cmd(DMA2_Channel6, ENABLE); // 开 DMA
return SET;
}
/*******************************************************************************
* Function Name : uartWriteWiFiStr
* Description : send string to ESP8266 via UART6 向 WiFi 模组发送字符串
* Input : char * str string to send
* Return : RESET UART busy,failed to send 发送失败
* SET send success 发送成功
*******************************************************************************/
FlagStatus uartWriteWiFiStr(char * str)
{
uint16_t num = 0;
while(str[num])num++; // 计算字符串长度
return uartWriteWiFi(str,num);
}
/*******************************************************************************
* Function Name : uartReadWiFireceive
* Description : read some bytes from receive buffer 从接收缓冲区读出一组数据
* Input : char * buffer buffer to storage the data 用来存放读出数据的地址
* uint16_t num number of data to read 要读的字节数
* Return : int number of bytes read 返回实际读出的字节数
*******************************************************************************/
uint16_t rxBufferReadPos = 0; //接收缓冲区读指针
uint32_t uartReadWiFi(char * buffer , uint16_t num)
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel7); //计算 DMA 数据尾的位置
uint16_t i = 0;
if (rxBufferReadPos == rxBufferEnd){
// 无数据,返回
return 0;
}
while (rxBufferReadPos!=rxBufferEnd && i < num){
buffer[i] = RxBuffer[rxBufferReadPos];
i++;
rxBufferReadPos++;
if(rxBufferReadPos >= RXBUF_SIZE){
// 超出缓冲区,回零
rxBufferReadPos = 0;
}
}
return i;
}
/*******************************************************************************
* Function Name : uartReadByteWiFi
* Description : read one byte from UART buffer 从接收缓冲区读出 1 字节数据
* Input : None
* Return : char read data 返回读出的数据(无数据也返回0)
*******************************************************************************/
char uartReadByteWiFi()
{
char ret;
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel7);
if (rxBufferReadPos == rxBufferEnd){
// 无数据,返回
return 0;
}
ret = RxBuffer[rxBufferReadPos];
rxBufferReadPos++;
if(rxBufferReadPos >= RXBUF_SIZE){
// 超出缓冲区,回零
rxBufferReadPos = 0;
}
return ret;
}
/*******************************************************************************
* Function Name : uartAvailableWiFi
* Description : get number of bytes Available to read from the UART buffer 获取缓冲区中可读数据的数量
* Input : None
* Return : uint16_t number of bytes Available to read 返回可读数据数量
*******************************************************************************/
uint16_t uartAvailableWiFi()
{
uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA2_Channel7);//计算 DMA 数据尾的位置
// 计算可读字节
if (rxBufferReadPos <= rxBufferEnd){
return rxBufferEnd - rxBufferReadPos;
}else{
return rxBufferEnd +RXBUF_SIZE -rxBufferReadPos;
}
}
这部分其实和之前蓝牙教程里面的通过DMA进行蓝牙模块的读取和写入差不多,仅将蓝牙模块的UART7变为WiFi模块的UART6,不再赘述。
string.h相关部分
从上面AT指令部分可以看出从网络上获取的时间数据是一长串字符串,所以我们需要string.h库,通过该库中的各种操作字符串数组的函数对数据进行分割,获取我们需要的数据。
strstr()
char *strstr(const char *haystack, const char *needle)
其中:
strcpy()
char *strcpy(char *dest, const char *src)
其中:
strncpy()
char *strncpy(char *dest, const char *src, size_t n)
其中:
dest -- 指向用于存储复制内容的目标数组;
src -- 要复制的字符串;
n -- 要从源中复制的字符数。
该函数返回最终复制的字符串。
定时器相关
typedef struct _TDateTime //时间结构体
{
char hour;
char minute;
char second;
char msec;
}TDateTime;
char timeout = 1; //对时标志位
/********************************************************************
* 函 数 名 : TIM6_Init
* 函数功能 : 初始化 定时器 TIM6
* 输 入 : arr:计数值,psc 预分频系数
* 输 出 : 无
********************************************************************/
void TIM6_Init( u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM6, ENABLE );
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Down;
TIM_TimeBaseInit( TIM6, &TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
TIM_ARRPreloadConfig( TIM6, ENABLE );
TIM_Cmd( TIM6, ENABLE );
}
/********************************************************************
* 函 数 名 : Interrupt_Init
* 函数功能 : 初始化定时器中断
* 输 入 : 无
* 输 出 : 无
********************************************************************/
void Interrupt_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure={0};
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/********************************************************************
* 函 数 名 : system_time_increase
* 函数功能 :时间进位
* 输 入 : 无
* 输 出 : 无
********************************************************************/
TDateTime TIME;
void system_time_increase(void)
{
TIME.msec++;
if(TIME.msec >= 2) //0.5s进入中断一次,2次就是1s
{
TIME.msec = 0;
TIME.second++;//秒进位
if(TIME.second >= 60)
{
TIME.second = 0;
TIME.minute++;//分钟进位
if(TIME.minute >= 60)
{
TIME.minute = 0;
TIME.hour++;//小时进位
if(TIME.hour >= 24)
{
TIME.hour = 0;
timeout = 1; //一天结束对一次时,并且把日期换好
}
}
}
}
}
/********************************************************************
* 函 数 名 : TIM6_IRQHandler
* 函数功能 : 中断服务程序的函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
void TIM6_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void TIM6_IRQHandler(void)
{
TIM_ClearFlag(TIM6, TIM_FLAG_Update);//清除标志位
system_time_increase(); //时间走动
}
题外话:本来这部分是不存在的,本人在这里比较耿直,时钟的实现靠着每0.5s向网站发送请求,来更新时间显示在屏幕上,短时间请求过多,被网站屏蔽了2h(悲),大家千万不要这么干。
在写定时器的程序之前,这里默认大家用的都是MRS,请按照置顶教程中的第四篇时钟:传送门的基础知识部分,设定一下参数,外部晶振频率设置为144MHZ,晶振启动超时阈值改为0x4000;
之后是定时器初始化和中断初始化程序,基础教程中有涉及就不展开了。
最后是走时逻辑,比较好的一点在于判断逻辑使用的是>=而非直接==,可以防止之后很多错误的产生。
数据分析部分
void get_true_time(void)
{
char *begin1; //日期与时间的起始地址
char *begin2; //星期的起始地址
char *end2; //星期的结束地址
char Time[100]={"\0"}; //时间字符串数组
char Date[100]={"\0"}; //日期字符串数组
char Week[100]={"\0"}; //星期字符串数组
//访问该网站会反馈时间数据
while(uartWriteWiFiStr("GET /?app=life.time&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json HTTP/1.1\r\n"
"Host: api.k780.com:88\r\n"
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0\r\n"
"\r\n")==RESET);
Delay_Ms(500);
int num = uartAvailableWiFi();
if (num > 0 ){
char buffer[1024]={"\0"};
uartReadWiFi(buffer , num); //读取数据
printf("Revceived:\r\n%s",buffer);
begin1 =strstr(buffer,"datetime_1");
strncpy(Date,begin1+13,10); //日期
Date[10] = '\0';
strncpy(Time,begin1+24,8); //时间
Time[10] = '\0';
begin2 =strstr(buffer,"week_4"); //星期
strcpy(Week,begin2+9);
end2 =strstr(Week,"\"");
*end2 = '\0';
printf("date:\r\n%s %s %s\n",Date,Time,Week); //在串口显示正确的时间
printf("\n");
lcd_set_color(BLACK,GREEN); //把日期屏幕显示
lcd_show_string(0, 50, 24,"%s", Date);
lcd_set_color(BLACK,BLUE); //把星期屏幕显示
lcd_show_string(15, 100, 24,"%-9s", Week);
TIME.hour = ((*(begin1+24) - 0x30)*10 + (*(begin1+25) - 0x30)) % 24; //把小时由字符串转为十进制数
TIME.minute = ((*(begin1+27) - 0x30)*10 + (*(begin1+28) - 0x30)) % 60; //把分钟由字符串转为十进制数
TIME.second = ((*(begin1+30) - 0x30)*10 + (*(begin1+31) - 0x30)) % 60; //把秒由字符串转为十进制数
}
}
- 首先我们知道只要打开透传模式之后,只要继续发送GET空格加网址就可以不停的获取时间数据,所以每次启动对时函数时直接发送该AT指令即可获取正确的时间数据;
- 然后就是读取函数,将读取到的字符串保存在buffer数组中;
{"success":"1","result":{"timestamp":"1658211128","datetime_1":"2022-07-19 14:12:08","datetime_2":"2022骞?7鏈?9鏃?14鏃?2鍒?8绉?,"week_1":"2","week_2":"鏄熸湡浜?,"week_3":"鍛ㄤ簩","week_4":"Tuesday"}}
- 然后用strstr()函数对buffer数组进行裁剪,找到数据中datetime_1后面即为所要的日期加时间,week_4后面为所要的星期。
- 通过strncpy()和strcpy()函数提取我们需要的部分的字符串,并把时间部分的字符串转为十进制数(0x30即为ASCII码中的“0”)方便后面的走时程序;
- 接下来只要保证每天0点对时将日期和星期的参数作为字符串显示即可简单的显示正确的时间。
main函数
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断控制器的优先级分组为 占优先级 2位 ,优先级2位。
TIM6_Init( 5000-1, 14400-1 ); // 初始化定时器,让 时间 1 秒走一次,我们需要让定时器 0.5 秒溢出,要计数 `144M * 0.5 = 72M` 个时钟周期,而定时器只有16位,这是不够的。需要用到预分频器,设分频系数为 14400,可以得到 10KHz 的定时器时钟,这样设置计数值 5000 就可以做到 0.5 ms 定时。
Interrupt_Init();//初始化定时器中断
Delay_Init();
USART_Printf_Init(115200); //初始化串口并设置波特率为115200
printf("SystemClk:%d\r\n",SystemCoreClock);
printf("8266 WiFi TEST\r\n");
DMA_INIT();
USARTx_CFG(); /* USART INIT */
USART_DMACmd(UART6,USART_DMAReq_Tx|USART_DMAReq_Rx,ENABLE);
lcd_init(); //初始化LCD屏幕
lcd_set_color(BLACK,WHITE);
lcd_show_string(50, 0, 32,"Yuimerlin");
lcd_set_color(BLACK,RED);
lcd_show_string(0, 32, 16,"WIFI clock Test");
delay_ms(100);
开始先初始化所有模块,设定好定时器。
Delay_Ms(1000);
// 查询 WiFi 模块是否正常工作
uartWriteWiFi("AT\r\n",4);
Delay_Ms(100);
// 查询 打开AT回显
uartWriteWiFi("ATE1\r\n",6);
Delay_Ms(100);
// // 设为 Station 模式
// while(uartWriteWiFiStr("AT+CWMODE=1\r\n")==RESET);
// Delay_Ms(100);
// // 复位WiFi模块
// while(uartWriteWiFiStr("AT+RST\r\n")==RESET);
// Delay_Ms(300);
// 设为单连接模式
while(uartWriteWiFiStr("AT+CIPMUX=0\r\n")==RESET);
Delay_Ms(100);
// 连接一个名为 SSID、密码为 PASSWORD 的 WiFi 网络,
while(uartWriteWiFiStr("AT+CWJAP=\"SSID\",\"PASSWORD\"\r\n")==RESET);
Delay_Ms(4000);
//打印之前收到的信息
int num = uartAvailableWiFi();
if (num > 0 ){
char buffer[1024]={"\0"};
uartReadWiFi(buffer , num);
printf("Revceived:\r\n%s",buffer);
}
//等待后续回复
while(uartAvailableWiFi()==0);
Delay_Ms(2000);
num = uartAvailableWiFi();
if (num > 0 ){
char buffer[1024]={"\0"};
uartReadWiFi(buffer , num);
printf("Revceived:\r\n%s",buffer);
}
Delay_Ms(5000);
//连接服务器
while(uartWriteWiFiStr("AT+CIPSTART=\"TCP\",\"api.k780.com\",80\r\n")==RESET);
Delay_Ms(500);
//开启透传模式
while(uartWriteWiFiStr("AT+CIPMODE=1\r\n")==RESET);
Delay_Ms(100);
//AT+CIPMODE=1并且作为客户端模式下,进入透传模式(需要支持硬件流控,否则大量数据情况下会丢数据)模块收到指令后先换行返回”>”,然后会发送串口接收到的数据。
while(uartWriteWiFiStr("AT+CIPSEND\r\n")==RESET);
Delay_Ms(100);
AT指令设定好WiFi模块,一直到我们之前测试的透传开始那一步。其中热点名称和密码为你自己想连的热点,把SSID改成你想连的热点名称,PASSWORD为你连热点的密码,由于连接上热点需要预留的时间有点长,读取一次不能完全接收完,所以要读取两次。
while(1)
{
if (timeout == 1) //根据标志位来对时
{
get_true_time();
timeout = 0;
}
lcd_set_color(BLACK,WHITE); //把时间屏幕显示
lcd_show_string(60, 150, 32,"%02d:%02d:%02d", TIME.hour,TIME.minute,TIME.second);
}
主循环部分仅为对时部分和显示走时,到此,自动上网对时的时钟就完成了。
显示效果
PS:测试中修改程序之后请重新上电,断电后等一会再上电,保证WiFi断开,由于我们程序中并未退出过透传,单纯点RST也没用,除非你在串口助手上看到busy的反馈的时候,请多按几次RST再像之前一样重新上电。
后话
到此WiFi模块的时钟例程就结束,希望这篇教程对您的赤菟板外接的WiFi模块连接的的学习有所帮助。这次的教程仅说明了大家可能会用的较多的部分,细致的地方没有说到的还请谅解。基本上所有出现的问题在数据手册上均有涉及,上面的介绍也相当完整,如果想深入探究可以作为参考。
老规矩,这次教程所涉及到的程序和之前一样放到了gitee仓库上文件名为:
欢迎大家参考!