- 移植本意应该是指通过修改,使得运行原先在另一个平台可运行的程序。
- STM32CubeMx 本文简称 Mx
麒麟座的例程使用的是标准库,ST后续推出更新的 LL库 和 HAL库,其中LL库与标准库类似,而 HAL 则更倾向于兼容通用性,体积和效率则没有优势,如果你想在自己的工程中使用麒麟座的例程,那么移植工作是难以避免的。
移植工作主要是硬件的设配,所以首先需要了解麒麟镇开发板例程使用的基本情况:
STM32F1 | |
---|---|
Uart1 | 输出打印口 |
Uart2 | ESP8266 串口 |
GPIOB.5 | ESP8266 复位 |
在本文工程中,硬件配置如下:
STM32F1 | |
---|---|
Uart1 | ESP8266 串口 |
SWO | 输出调试口 |
GPIOB.5 | ESP8266 复位 |
我移植工程时,通常先做一部分的准备工作,再开始具体针对硬件特性的移植:
准备工作:
mian.h
包含了外设头文件,所以 标准中所有类似 #include "usart.h"
之类的头文件就应该换成 mian.h
,另外,stm32f10x.h
是标准库中关于外设地址的共定义,若使用 Mx 生成的工程,该文件名为 stm32f106xb.h
,它被mian.h
包含了(不是直接包含),所以,#include "stm32f10x.h"
语句也是需要替换为include "mian.h"
语句。DelayXms();
,需要替换为目标工程库函数的 HAL_Delay()
,单位不需换算,因为都是毫秒。接下来是具体的移植。
新建一个基础工程与初始化外设,使用的是 Mx ,此处不再赘述,在麒麟镇中,需要移植的源码都在其工程目录[NET]文件夹中,将其添加到本文工程如下所示:
接着添加头文件路径:
如前文所述,本文使用 Mx 工程,所以外设头文件统一为 main.h
,所以将esp8266.c
和 onenet.c
中头文件替换:
麒麟镇使用以下语句打印信息:
UsartPrintf(USART_DEBUG, "OneNet_SendData\r\n");
而本文使用:
printf("OneNet_SendData\r\n");
所以直接查找替换:
将所有 UsartPrintf(USART_DEBUG,
替换成printf(
:
程序处理了字符串,最后是通过以下函数将数据发送到 ESP8266 的:
void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
{
unsigned short count = 0;
for(; count < len; count++)
{
USART_SendData(USARTx, *str++); //发送数据
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //等待发送完成
}
}
本文将其改为:
void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
{
unsigned short count = 0;
for(; count < len; count++)
{
LL_USART_TransmitData8(USARTx, *str++); //发送数据
while(!LL_USART_IsActiveFlag_TC(USARTx)); //等待发送完成
}
}
注意到,这个函数在源工程的 usart.c
中,这文件只需要这个函数,所以不需要复制整个文件,只需要把和这个函数添加到 esp8266.c
中即可。另外,这函数需要第一个参数来确定串口设备,而本文使用的是串口1,源工程使用功串口2,所以工程中,所有的语句:
Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
需要改为:
Usart_SendString(USART1, (unsigned char *)cmd, strlen((const char *)cmd));
esp8266 使用串口中断来接收数据:
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
{
if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆
esp8266_buf[esp8266_cnt++] = USART2->DR;
USART_ClearFlag(USART2, USART_FLAG_RXNE);
}
}
本文使用的是串口1,硬件也接入串口1,所以发生中断会进入串口1中断:
//文件:stm32f10x_it.c
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
extern void ESP8266_IRQHandler(void);
ESP8266_IRQHandler();
/* USER CODE END USART1_IRQn 0 */
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
通过调用ESP8266_IRQHandler()
的方法,是程序处理可以写在其他文件中:
//文件:esp8266.c
void ESP8266_IRQHandler(void)
{
if(LL_USART_IsActiveFlag_RXNE(USART1)) //接收中断
{
if(esp8266_cnt >= sizeof(esp8266_buf))
{
esp8266_cnt = 0; //防止串口被刷爆
}
esp8266_buf[esp8266_cnt++] = USART1->DR;
LL_USART_ClearFlag_RXNE(USART1);
}
}
源文件 esp8266 初始化中初始化了所使用的串口,但在 Mx 在已经初始化了,所以需要删除,顺便将复位信号改正:
void ESP8266_Init(void)
{
// GPIO_InitTypeDef GPIO_Initure;
//
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// //ESP8266复位引脚
// GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;
// GPIO_Initure.GPIO_Pin = GPIO_Pin_5; //GPIOB5-复位
// GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_Init(GPIOB, &GPIO_Initure);
//
// GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_RESET);
// HAL_Delay(250);
// GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_SET);
// HAL_Delay(500);
HAL_GPIO_WritePin(ESP_RST_GPIO_Port,ESP_RST_Pin,GPIO_PIN_RESET);
HAL_Delay(250);
HAL_GPIO_WritePin(ESP_RST_GPIO_Port,ESP_RST_Pin,GPIO_PIN_SET);
HAL_Delay(500);
ESP8266_Clear();
printf("1. AT\r\n");
while(ESP8266_SendCmd("AT\r\n", "OK"))
HAL_Delay(500);
printf("2. CWMODE\r\n");
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
HAL_Delay(500);
printf("3. CWJAP\r\n");
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
HAL_Delay(500);
printf("4. CIPSTART\r\n");
while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
HAL_Delay(500);
printf("5. ESP8266 Init OK\r\n");
}
OneNet_RevPro()
函数响应数据的内容,并打开和关闭LED灯,本文并没有对应的接口,所以将打开 LED 灯接口都换成打印:
另外,源文件发送数据是led状态:
unsigned char OneNet_FillBuf(char *buf)
{
char text[16];
memset(text, 0, sizeof(text));
strcpy(buf, "{");
memset(text, 0, sizeof(text));
sprintf(text, "\"Red_Led\":%d,", led_status.Led5Sta);
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"Green_Led\":%d,", led_status.Led4Sta);
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"Yellow_Led\":%d,", led_status.Led3Sta);
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"Blue_Led\":%d", led_status.Led2Sta);
strcat(buf, text);
strcat(buf, "}");
return strlen(buf);
}
这里你可以修改为任何值,然后看看效果如何。
本文测试时候未接复位IO口,完成以上操作后,可以通过onenet平台下发命令来测试通信,根据说明文档:
1.修改esp8266.c下的wifi账号及密码
2.修改onenet.c下的proid、auth_info和devid
3.指令说明:
1.命令直接下发:
redled:1 打开红灯
greenled:1 打开绿灯
yellowled:1 打开黄灯
blueled:1 打开蓝灯
同理,1替换为0则是关闭
2.应用命令填写方式:
redled:{V}, 控制红灯;开关开值1,开关关值0
greenled:{V}, 控制绿灯;开关开值1,开关关值0
yellowled:{V}, 控制黄灯;开关开值1,开关关值0
blueled:{V}, 控制蓝灯;开关开值1,开关关值0
链接:onenet_stm32_mqtt
提取码:1234