目錄
前言
一、stm32cubeMX的串口配置
二、空閑中斷+dma接收
三、ESP8266.c和ESP8266.h
ESP8266.h
ESP8266.c
注意事項
四、與手機通信例程
步驟:
?例程代碼main.c
運行結果
五、相關問題
總結
相關的app和源碼
?
?
?
?
前言
前提:
1.掌握串口通信和ESP8266的使用方法
串口通信:單片機串口通信不理解?STM32的USART和UART差在哪里?幾分鐘給你講清楚?。⊿TM32教程基于HAL庫和CUBEIDE)_嗶哩嗶哩_bilibili
ESP8266的使用
?STM32HAL庫使用ESP8266模塊_hal esp8266_啵啵520520的博客-CSDN博客
2.這次采用的空閑中斷+dma來和ESP8266通信,接收ESP8266發(fā)來的不定長數(shù)據(jù)
?STM32 hal庫串口空閑中斷最新用法_hal 串口空閑中斷_北世安的博客-CSDN博客
3.板子是魔女開發(fā)板,主控芯片是STM32F103RCT6
4.文章末尾會附帶我寫好的重定向和esp8266通信代碼,調(diào)用api即可
?
一、stm32cubeMX的串口配置
采用串口1與pc通信
采用串口3與8266通信
開啟dma和中斷,把串口3的rx的dma通道優(yōu)先級調(diào)高(比串口1的rx的dma通道的優(yōu)先級高就行)
如果沒調(diào)高,可能會出現(xiàn)沒能及時收到8266回復的信息的問題。
?
二、空閑中斷+dma接收
?
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,PCRxbuf,sizeof(PCRxbuf));//使能空閑dma中斷
HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)//中斷回調(diào)函數(shù)
當開啟空閑接收中斷后,當2個字節(jié)的時間內(nèi)沒有新的數(shù)據(jù)來,或者到達你設置的最大接收值,就會進入到這個中斷回調(diào)函數(shù)中,其中Size就是此次接收到的函數(shù)
三、ESP8266.c和ESP8266.h
ESP8266.h
#ifndef _ESP_8266_H
#define _ESP_8266_H
#include "main.h"
#include <stdio.h>
#define LENGTH 1024
extern uint8_t ESP8266Rxbuf[LENGTH];//存放來自8266的回復
void ESP8266_Init(UART_HandleTypeDef *uart);//初始化函數(shù)
uint8_t sendATTo8266(uint8_t *str);//向8266發(fā)送AT指令
uint8_t sendStringTo8266(uint8_t *str);//向8266發(fā)送字符串
void recStringBy8266(uint16_t Size);//接收來自8266的字符串
void linkWifi(char *ssid,char *pwd);//連接wifi,ssid wifi:名字,pwd:wifi密碼
void linkTCP(char *addr,char *port);//建立TCP連接,addr:IP地址 port:端口
void StartCipsend(char *addr,char *port);//開始透明傳輸
#endif
?
ESP8266.c
#include "ESP8266.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include "string.h"
UART_HandleTypeDef *uart8266;
uint8_t ESP8266Rxbuf[LENGTH];//存放8266的消息
uint8_t CIPMODE=0;//透明傳輸模式標志位
uint8_t TESTMODE=0;//測試模式標志位
int replyFlag=0;//8266回復標志位
/*8266初始化*/
void ESP8266_Init(UART_HandleTypeDef *uart){
uart8266=uart;
HAL_Delay(2000);//8266上電后要等待一段時間才能正常運行
HAL_UART_Transmit(uart8266,"AT+RESTORE\r\n",12,0xff);//發(fā)送恢復出廠化設置的指令
HAL_Delay(5000);//等待恢復出廠設置完成
HAL_UARTEx_ReceiveToIdle_DMA(uart8266,ESP8266Rxbuf,sizeof(ESP8266Rxbuf));//開啟接收8266的消息
}
/*檢查AT指令是否合規(guī)*/
/*0 不合規(guī) 1 合規(guī)*/
uint8_t checkAT(uint8_t *str,int len){
int i=0;
for(i=0;i<len;i++){
if(*(str+i)=='\r'||*(str+i)=='\n'){
if(i<len-2){
return 0;
}
else if(*(str+i)=='\r'&&*(str+i+1)=='\n'){
return 1;
}
}
}
return 0;
}
/*發(fā)送AT指令*/
/*0 發(fā)送失敗,1 發(fā)送成功*/
uint8_t sendATTo8266(uint8_t *str){
int len=strlen((char *)str);
if(0==checkAT(str,len)){
printf("AT error\n");
return 0;
}
else{
printf("AT right\n");
if(HAL_OK==HAL_UART_Transmit(uart8266,str,len,0xff)){
printf("%.*s send to 8266 ok!\n",len-2,str);
return 1;
}
else{
printf("%.*s send to 8266 error!\n",len-2,str);
return 0;
}
}
}
/*接收來自8266的AT消息回復*/
void recATBy8266(uint16_t Size){
HAL_UART_DMAStop(uart8266);//先暫停接收
// printf("wifibuf:%s\n",WifiRxbuf);
int i=0;
do{ //判斷消息里有沒有OK或者ERROR,8266回復的消息里都會帶有OK或者ERROR
if(ESP8266Rxbuf[i]=='O'&&ESP8266Rxbuf[i+1]=='K'){
memset(ESP8266Rxbuf,0,sizeof(ESP8266Rxbuf));
HAL_Delay(2000);//收到OK,延時,等后續(xù)的消息發(fā)送完成
printf("8266 say OK\r\n");
replyFlag=1;//收到回復的標志位置1
// printf("OK replyFlag:%d\n",replyFlag);
HAL_UARTEx_ReceiveToIdle_DMA(uart8266,ESP8266Rxbuf,sizeof(ESP8266Rxbuf));//開啟下次接收
return;
}
else if(ESP8266Rxbuf[i]=='E'&&ESP8266Rxbuf[i+1]=='R'&&ESP8266Rxbuf[i+2]=='R'&&ESP8266Rxbuf[i+3]=='O'&&ESP8266Rxbuf[i+4]=='R'){
HAL_UART_Transmit(uart8266,"AT\r\n",4,0xff);//如果返回是error,那么下一條還是發(fā)送失敗,所以用個AT指令來抵掉這次發(fā)送失敗
HAL_Delay(4000);//延時等抵消成功
printf("\n8266 say ERROR\r\n");
replyFlag=2;//收到回復的標志位置2
// printf("ERROR replyFlag:%d\n",replyFlag);
memset(ESP8266Rxbuf,0,sizeof(ESP8266Rxbuf));
HAL_UARTEx_ReceiveToIdle_DMA(uart8266,ESP8266Rxbuf,sizeof(ESP8266Rxbuf));
return;
}
}while(++i<Size);
/*8266回復的消息有些是分包發(fā)回的,即OK或者ERROR可能在后續(xù)的包里面*/
memset(ESP8266Rxbuf,0,sizeof(ESP8266Rxbuf));//清空接收下一數(shù)據(jù)包
HAL_UARTEx_ReceiveToIdle_DMA(uart8266,ESP8266Rxbuf,sizeof(ESP8266Rxbuf));//開始接收下一數(shù)據(jù)包
}
/*向8266發(fā)送字符串*/
/*0:發(fā)送失敗,1:發(fā)送成功*/
uint8_t sendStringTo8266(uint8_t *str)
{
uint16_t Size = strlen((char *)str);
if(CIPMODE==1){//判斷是否為透明傳輸模式,是的話直接進行發(fā)送
HAL_UART_Transmit(uart8266,str,Size,0xff);
if(!strcmp((char *)str,"+++")){//如果發(fā)送的是“+++”(不帶\r\n就是關閉透明傳輸模式)
printf("\nclose CIPSEND\n");
CIPMODE=0;
}
return 1;
}
/*這個是我編寫的調(diào)試模式,不用發(fā)給8266*/
/*調(diào)試模式下,就相當于PC直接與8266通信*/
else if(!strcmp((char *)str,"AT+TESTMODE\r\n")){
if(TESTMODE==0){
printf("open testmode\n");
TESTMODE=1;
}
else{
printf("close testmode\n");
TESTMODE=0;
}
return 0;
}
if(HAL_OK==HAL_UART_Transmit(uart8266,str,Size,0xff)){//發(fā)送成功
if(!strcmp((char *)str,"AT+CIPSEND\r\n")){//判斷是否是開啟透明傳輸模式的指令
printf("open CIPSEND\n");
uint32_t t=0;
while(replyFlag==0){//等待8266回復
HAL_Delay(1000);
t++;
if(t>40){
printf("open CIPSEND error\n");
return 0;
}
}
CIPMODE=1;//收到回復就把標志位置1
}
return 1;
}
else{
return 0;
}
}
/*向8266發(fā)送消息并且確認回復*/
/*0:發(fā)送失敗或者沒收到回復或者回復ERROR*/
/*1:發(fā)送成功且收到OK回復*/
uint8_t sendTo8266(uint8_t *str){
if(sendStringTo8266(str)){
uint32_t t=0;
// uint32_t t=4000*3127;//臭狗屎,慎用,用于調(diào)試,看看回復標志位什么時候置1。
// while(t--){
// printf("t=%d flag=%d\n",t,replyFlag);
// }
while(replyFlag==0){//等待回復
HAL_Delay(1000);
t++;
if(t>40){//超時
printf("time out error\n");
return 0;
}
}
if(replyFlag==1){//判斷是不是OK回復。
replyFlag=0;
return 1;
}
else{
printf("error\n");
replyFlag=0;
return 0;
}
}
else{
replyFlag=0;
return 0;
}
}
/*處理來自8266的消息*/
void recStringBy8266(uint16_t Size)
{
if(CIPMODE!=0||TESTMODE!=0){//如果是透明傳輸模式或者調(diào)試模式,直接顯示
printf("%s\n",ESP8266Rxbuf);
memset(ESP8266Rxbuf,0,sizeof(ESP8266Rxbuf));
HAL_UARTEx_ReceiveToIdle_DMA(uart8266,ESP8266Rxbuf,sizeof(ESP8266Rxbuf));
}
else{//否則就是處理AT消息回復
recATBy8266(Size);
}
}
/*連接wifi*/
/*過程
1.設置為STA模式:AT+CWMODE=1\r\n
2.重啟8266模塊執(zhí)行設置:AT+RST\r\n
3.連接wifi:AT+CWJAP="wifi名","密碼"\r\n
*/
void linkWifi(char *ssid,char *pwd){
printf("Start link wifi:\n");
printf("Set CWMODE=1\n");
if(sendTo8266("AT+CWMODE=1\r\n")){
printf("Set CWMODE success!\n");
}
else{
printf("Set CWMODE fail!\n");
}
printf("8266 RESTART\n");
if(sendTo8266("AT+RST\r\n")){
printf("8266 RESTART success!\n");
}
else{
printf("8266 RESTART fail!\n");
}
printf("link wifi:%s %s\n",ssid,pwd);
char str[50];//一定要是char類型的數(shù)組,否則sprintf會報錯,不知道怎么解決
sprintf(str,"AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,pwd);//在stdio.h庫中
if(sendTo8266(str)){
printf("link wifi success!\n");
}
else{
printf("link wifi fail!\n");
}
}
/*建立TCP連接*/
void linkTCP(char *addr,char *port){
printf("link TCP: %s %s\n",addr,port);
char str[50];
sprintf(str,"AT+CIPSTART=\"TCP\",\"%s\",%s\r\n",addr,port);
if(sendTo8266(str)){
printf("link tcp success!\n");
}
else{
printf("link tcp fail!\n");
}
}
/*開始透明傳輸*/
/*過程
1.連接TCP
2.設置為透明傳輸模式:AT+CIPMODE=1\r\n
3.開啟透明傳輸:AT+CIPSEND\r\n
*/
void StartCipsend(char *addr,char *port){
linkTCP(addr,port);
printf("Set Cipmode=1\n");
if(sendTo8266("AT+CIPMODE=1\r\n")){
printf("Set Cipmode=1 success!\n");
}
else{
printf("Set Cipmode=1 fail!\n");
}
printf("Start cipsend\n");
if(sendTo8266("AT+CIPSEND\r\n")){
printf("Start cipsend success!\n");
}
else{
printf("Start cipsend fail!\n");
}
}
注意事項
里面用到了prinf向PC機發(fā)送信息,所以要使printf重定向到串口1
可以參考(printf和scanf的串口重定向,格式化輸入輸出_scanf重定向到串口_螞蟻爬爬爬的博客-CSDN博客
或者直接用我后面提供的OutIn.c和OutIn.h就可以
(注:我這用的是標準庫方式重定向,也就是方法2,記得不要勾選MicorLib,還有修改和主機通信的串口)
?
?
四、與手機通信例程
步驟:
1.手機和開發(fā)板連接同個wifi,如果是熱點的話,一定要設為2.4GHz頻段
2.手機安裝“TCP鏈接”app,開啟口,應用上方可以看到IP地址和端口號
3.修改例程里的相關參數(shù),編譯下載燒錄即可
?
?
?例程代碼main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
#include "stdio.h"
#include "ESP8266.h"
#include "OutIn.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t PCRxbuf[LENGTH];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_Delay(uint32_t Delay){//用自己寫的延時,這樣就能在中斷回調(diào)里使用
uint32_t t=Delay*3127;
while(t--);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,PCRxbuf,sizeof(PCRxbuf));
ESP8266_Init(&huart3);
/* USER CODE END 2 */
linkWifi("Lzl","L123456@");
StartCipsend("192.168.21.243","1234");
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){
if(huart==&huart1)
{
// printf("tx size : %d\n",Size);
sendStringTo8266(PCRxbuf);
// printf("text:%s\n",PCRxbuf);
// printf("sizeof:%d strlen:%d\n",sizeof(PCRxbuf),strlen(PCRxbuf));
memset(PCRxbuf,0,sizeof(PCRxbuf));
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,PCRxbuf,sizeof(PCRxbuf));
// printf("huart1 dma open!\n");
}
if(huart==&huart3){
// printf("size:%d\n",Size);
recStringBy8266(Size);
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
運行結果
?
?
五、相關問題
1.正常情況下,在中斷回調(diào)函數(shù)的延時函數(shù)是會出現(xiàn)問題的
詳細請參考:(STM32 HAL庫 中斷處理中使用延時函數(shù)(HAL_Delay)_中斷中延時_Dir_xr的博客-CSDN博客
所以我們在主函數(shù)里寫一個延時函數(shù)
2.當給8266發(fā)送重啟或者恢復出廠化等一些指令時,8266在回復OK后,還會回復一些其他東西,其中就有些亂碼,導致的問題是,接收到2個字符的亂碼,無法進入中斷回調(diào)函數(shù),但是會失能你的接收,所以就沒法再接收8266的信息了,就要再次使能接收才行。你可以進入測試模式,或者直接硬件連接8266與其直接通信,看看效果。
這個問題我一直都還不懂怎么解決,我采用的方法是延時一段時間,等OK后的亂碼消息發(fā)送完后再使能接收,治標不治本,后面解決了會更新這篇文章。
3.對于不同指令,8266回復時間不一樣,也就是說有些能很快收到OK或者ERROR,有些就不行,代碼里我給的是40s內(nèi),如果后面出現(xiàn)replayFlag與你想要的效果不一樣,可以取消掉我那幾條pintf和那個臭狗屎代碼來測試問題在哪,大概率是時間問題,改成40s以上應該會解決,看個人實際情況,來修改我代碼中的一些延時來提高通信速率。
總結
相關的app和源碼
鏈接:https://pan.baidu.com/s/17Lzfma5iekRKTS21OrY9Qg?pwd=0407?
提取碼:0407
以上本文的內(nèi)容,只是簡單的與手機APP通信,后續(xù)會更新stm32hal庫與其他外設的使用或者其他內(nèi)容,有什么疑問可以私信。文章來源:http://www.zghlxwxcb.cn/news/detail-761498.html
如果這對你有用的話,麻煩點贊+收藏,這對我來說真的重要文章來源地址http://www.zghlxwxcb.cn/news/detail-761498.html
到了這里,關于入門stm32:STM32hal庫實現(xiàn)ESP8266與手機通信(不定長數(shù)據(jù)收發(fā)和ESP8266使用的一些問題)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!