上一篇
esp8266怎么配置
esp8266和USB转TTL连接
esp8266简单介绍
esp8266连接路由器
esp8266通过路由器连接在同一局域网中的电脑,建立TCP连接
esp8266使用串口发送数据到电脑上的网络调试助手
esp8266使用串口通过局域网发送到电脑上自己写的Java程序
esp8266的透传模式
esp8266使用透传模式连接到电脑的网络调试助手
esp8266使用透传模式连接到电脑上的java程序
这一篇
esp8266与STM32连接,电脑通过STM32配置esp8266实现联网发送数据
具体流程如下图
esp8266怎么和STM32连接(引脚连接)?
STM32CubeMx配置的usart2使用的PA2和PA3要与esp8266的TX和RX对应,但是得反过来接,要么根本发不出去,看来esp8266又印反了
3V3和EN得接到同一个3V上才可以,要不收到的老是error
STM32使用USART和电脑相互传输数据?
将STM32产生的数据发送到电脑的串口调试助手
第一种方式:
使用UASRT传输只需要重新定义fputc()函数,直接使用Printf函数就可以将字符串打印到电脑(即通过串口输出到电脑)
第二种方式:
使用HAL库中封装好的UASART函数 HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)也可以实现串口输出
电脑要发送字符串给STM32,那么STM32怎么收到数据,而且知道这个数据什么意思呢?
第一种方式(只能接收定长字符串):
HAL库依旧有封装好的函数HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout),但是使用这个函数有一个问题,这个函数的额第三个参数是收到的字符串的大小,可是我都没有发送,怎么提前把这个字符串的大小写入单片机呢?
其实问题是STM32如何接收不定长的字符串?
【STM32内部产生的数据或者从传感器中获取的数据,STM32如果要发送该数据可以直接使用sizeof获得数据长度作为第三个参数,但是当人为的发送给串口时,由于提前烧入的程序如果要使用HAL库的USART接收函数的话,需要确定一个字符串长度,那么这个程序就只能接收指定长度的字符串了,那要如何实现不定长也能接收呢?】
第二种方式:
DMA+接收结束判断。这接收结束判断可以是字节的timeout,也可以是USART_IT_IDLE线路空闲的标志等。这种方式对于需要整帧接收,且通信速度较快或需要占用cpu较少的情况
【详细的处理方式可见串口USART 收发数据处理方式总结--DMA+USART+USART_Idle_Interrupt】
那么如何配置呢?
既然是DMA和空闲接收中断就需要设置DMA和UASRT的中断(即在中断处理函数中需要有DMA1 channel5 global interrupt和USART1 global interrupt)
如果要使用printf输出到串口的话,就需要重定义fputc函数(定义在usart.c中)
如果要用线路空闲来触发中断来结束接收的话,需要有判断线路是否空闲的函数,这个函数最好还需要在结束接收时将收到的数据保存到自己定义的变量中
那么如何实现不定长接收呢,不还是得知道接受的字符串的长度吗?
是需要知道,这里使用一个结构体来保存字符串和字符串的长度以及一个标记(用来表示一次接收完成,如果不放在结构体里面,使用全局变量的话会比较麻烦)
具体的方法是这样的:在一开始接收的时候,将收到的字符串保存在结构体中的字符串数组中,而函数要求的大小这个参数设置为比较大的数字(可以是一次接收数据长度的最大值,这里是1024),在判断线路是否空闲的函数中,当线路空闲时(即一次接收完成时),将收到的字符串的长度保存到这个结构体中的字符串长度变量中,这样,当需要将收到的字符串再次发出时,就可以使用已知的长度进行发送了,由此实现了不定长接收。
【其原理其实是用一个大空间去接收(看来接收空间够大就行,不需要定长接收),在接收完成时计算收到的数据长度保存起来(和收到的数据要一一对应,这也是使用结构体的好处),当需要再次发送出去时,就知道这个字符串的长度了,就可以定长发送了】
结构体
#define RX_LEN 1024
typedef struct
{
uint8_t RX_flag:1; //IDLE receive flag
uint16_t RX_Size; //receive length
uint8_t RX_pData[RX_LEN]; //DMA receive buffer
}USART_RECEIVETYPE;
extern USART_RECEIVETYPE UsartType;
下面是程序的流程
首先在main函数中使用HAL_UART_Receive_DMA(&huart1, UsartType.RX_pData, RX_LEN); 开始接收,接收的数据放到结构体中,长度为最大值
然后打开中断,__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);这个函数进行中断使能,
具体介绍:
/** @brief Enable the specified UART interrupt.
* @param __HANDLE__: specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @param __INTERRUPT__: specifies the UART interrupt source to enable.
* This parameter can be one of the following values:
* @arg UART_IT_CTS: CTS change interrupt
* @arg UART_IT_LBD: LIN Break detection interrupt
* @arg UART_IT_TXE: Transmit Data Register empty interrupt
* @arg UART_IT_TC: Transmission complete interrupt
* @arg UART_IT_RXNE: Receive Data register not empty interrupt
* @arg UART_IT_IDLE: Idle line detection interrupt
* @arg UART_IT_PE: Parity Error interrupt
* @arg UART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
* @retval None
* */
#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
之后是否需要延时防止接收不到或者防止其他错误的发生不是很清楚。
在stm32f1xx_it.c的DMA和USART的中断函数中并没有需要重写的回调函数
在usart.c中实现处理空闲的函数添加到USART的中断函数中,那么当线路产生空闲时,就会触发一个中断,这个空闲处理的函数,就会开始工作,先清空空闲标志以允许继续接收,然后使用HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)停止此次接收,接着获得此次接收到的字符串的长度(这里比较深入,有寄存器的问题,不大理解),然后又调用了HAL_UART_Receive_DMA,这个应该和取得的长度是同一个结构体,那么之前的HAL_UART_Receive_DMA又是再干什么呢?(或许是为了解决第一次的传输问题,学的不够深入,还需要学的再深入)
最后就可以使用获得的长度和字符串将收到的字符串发送出去了,这样就实现了不定长接收。
【问题】在结构体中定义的是一个uint8_t类型的数组,那么可以按字符串输出吗?
可以,
HAL_UART_Transmit(&huart1, UsartType.RX_pData, UsartType.RX_Size, 0xFFFF);
printf("\n%s\n",UsartType.RX_pData);
上面两句一样的效果
具体代码:
在usart.c中添加
/* USER CODE BEGIN 1 */
/**
* @brief Retargets the C library printf function to the USART.
*/
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/**
* @brief This function handles USART1 IDLE interrupt.
*/
void UsartReceive_IDLE(UART_HandleTypeDef *huart)
{
uint32_t temp;
if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_DMAStop(&huart1);
temp = huart1.hdmarx->Instance->CNDTR;
UsartType.RX_Size = RX_LEN - temp;
UsartType.RX_flag=1;
HAL_UART_Receive_DMA(&huart1,UsartType.RX_pData,RX_LEN);
}
}
/* USER CODE END 1 */
在usart.c中实例化结构体
/* USER CODE BEGIN 0 */
USART_RECEIVETYPE UsartType;
/* USER CODE END 0 */
在usart.h中定义结构体
/* USER CODE BEGIN Private defines */
#define RX_LEN 1024
typedef struct
{
uint8_t RX_flag:1; //IDLE receive flag
uint16_t RX_Size; //receive length
uint8_t RX_pData[RX_LEN]; //DMA receive buffer
}USART_RECEIVETYPE;
extern USART_RECEIVETYPE UsartType;
/* USER CODE END Private defines */
在stm32f1xx_it.c的中断函数中添加
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
UsartReceive_IDLE(&huart1);
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
在main.c中main函数中基本的初始化函数后添加
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA(&huart1, UsartType.RX_pData, RX_LEN);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_Delay(1000);
/* USER CODE END 2 */
STM32如何给esp8266发送AT指令?
途径就为通过串口
那么怎么使用串口,
是直接使用HAL库封装的相关函数
还是需要一些驱动代码
电脑通过串口与STM32交流,STM32通过串口与esp8266交流
既然上面实现了电脑与STM32使用USART相互传输数据,即STM32可以接收电脑发来的不定长数据,那么STM32也能接收esp8266返回的不定长数据(向esp8266发送不同的指令,返回消息不一样,长度也不一样)
对于接收esp8266返回的信息也使用DMA加空闲中断的方式,那么向esp8266发送数据要怎么做呢?
上面的代码已经可以接收来自电脑的不定长消息,然后再发给电脑,那么将这个消息直接发送给esp8266就可以了,两个不同的串口有不同的实例,只需要指定好串口就可以向这个串口发送数据了。【突然觉得自己写的好乱,没有条理,得改】
其实具体的代码就是在什么那个处理STM2与电脑使用USART通信的代码上再增加一个STM与esp8266的USART交流流程就可以了,需要注意区分不同的串口,两个流程的交汇点在main函数中,当STM32收到电脑发来的不定长信息后,通过空闲中断处理函数获得长度,将该消息直接使用串口发送到连接esp8266的串口即可,如果STM32收到来自esp8266串口的消息,一样使用DMA加空闲中断的方式获得来自esp8266的不定长返回信息,在这里也是一样通过这个串口的空闲中断处理函数获得返回信息的长度,将这个返回信息发送给与电脑相连的串口就可以了,这样就完成了通过STM32配置esp8266的整个流程。
其意义是什么呢?
这样STM32本身就可以使用串口将获取的传感器信息通过esp8266发送到指定服务器,实现了STM32的联网,一样也实现了通过网络向STM32发送指令控制传感器的功能。