1、等待查询等待方式
void Usart_Send_Char(unsigned char *c,uint32_t cnt)//把发送数据的指针和个数传入,然后直到发送完才退出
{
while(cnt--)
{
USART_SendData(USART1, *c++);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET );//死等发送完成,TXE置1表示发送寄存器空,然后发送下一个
}
}
STM32 HAL库对应的查询发送函数:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)//在给定的时间内发送一帧数据,直到发送完成或超出时间,才会退出
对应的查询接收函数:
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)//接收指定长度个数据,如果没有接收够就不退出,除非超时
以上2个函数都需要输入4个参数:
1、串口号
2、要发送数据的位置 或 接收的数据存放的位置
3、数据长度,多少个字节
4、规定的时间,单位一般是ms, (uwTick的单位)
2、中断方式发送
中断方式就是把数据 地址放到指定的指针变量,然后打开中断。发送完一个字节后,会发生中断,中断会自动从指定的地址取下一个数来发送,直到发送完成指定长度,关中断。
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* Check that a Tx process is not already ongoing */
if(huart->gState == HAL_UART_STATE_READY)
{
if((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pTxBuffPtr = pData;//把要发送的数据指针放到指定位置
huart->TxXferSize = Size;//数据长度
huart->TxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Transmit data register empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TXE);//打开中断
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
中断接收:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
另外还有一个,处理串口中断请求的函数,就是中断后调用,该函数会判断是接收中断,还是发送中断…
HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
3、环形队列
1、如下建立三个数组,第1个要定义为volatile可变的,即每个读取的时候都要从实际地址中读取,防止keil优化。再定义3个全局变量
uint8_t volatile wifi_queue_buf[20];
uint8_t wifi_uart_rx_buf[30];
uint8_t wifi_uart_tx_buf[30];
volatile uint8_t *queue_in;
volatile uint8_t *queue_out;
volatile uint8_t queue_total_data;
2、在串口中断中,判断如果是接收中断,调用uart_receive_input(ch);将接收到的字节入队wifi_queue_buf[20],并将全局变量queue_total_data++,记录队列中的字节数
当然main.c初始化的时候要把queue_in,queue_out都指向队列的头部
void USART1_IRQHandler(void)
{
uint8_t ch;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
ch = USART_ReceiveData(USART1);
uart_receive_input(ch);
}
}
/***********************************************/
void uart_receive_input(unsigned char value)
{
if(queue_total_data < sizeof(wifi_queue_buf))
{
if(queue_in >= (unsigned char *)(wifi_queue_buf + sizeof(wifi_queue_buf)))//溢出,返回到开头
{
queue_in = (unsigned char *)(wifi_queue_buf);
}
*queue_in ++ = value;
queue_total_data ++;
}
else
{
//队列已满
}
}
以上完成了把数据接收,并放入到环形队列wifi_queue_buf[20]中,而且queue_total_data记录了队列里有多少数据
3、出队
在main.c的while(1)循环中,调用void wifi_uart_service(void)处理接收到的数据。
定义一个static uint8_t rx_in用于记录出队数量,必须定义为static,相当于全局变量,但作用范围只是本函数
void wifi_uart_service(void)
{
static uint8_t rx_in = 0;
uint8_t offset = 0;
uint8_t rx_value_len = 0;
while((rx_in < sizeof(wifi_uart_rx_buf)) && get_queue_total_data() > 0)//
{ //只要队列wifi_queue_buf中有数据,并且wifi_uart_rx_buf队列中有空位置,就把数据复制过来
wifi_uart_rx_buf[rx_in ++] = Queue_Read_Byte();
}
高用下面的函数复制队列数据,然后总数减1
unsigned char Queue_Read_Byte(void)
{
unsigned char value;
if(queue_total_data > 0)
{
//有数据
if(queue_out >= (unsigned char *)(wifi_queue_buf + sizeof(wifi_queue_buf)))
{
//数据已经到末尾
queue_out = (unsigned char *)(wifi_queue_buf);
}
value = *queue_out ++; //
queue_total_data --;
}
return value;
}
数据复制到wifi_uart_rx_buf[]后,我们就可以判断是否够一条指令,如果够,就处理。然后rx_in需要减去一条指令的长度,剩下如果还有数据,下次处理。
以上完成了接收处理。
4、发送,
把指令先存入到wifi_uart_tx_buf数组,然后调用发送函数发送。
以上 环形队列 参考了涂鸦科技的例程:http://www.tuya.com
4、DMA 发送方式
1、接收完一半数据的时候会中断,接收完再中断一次。每个DMA通道都有3个事件标志(DMA半传输,DMA传输完成,DMA传输出错);这三个事件标志或成为单独 中断源。DMA可以在传输过半,传输完成,传输错误时产生中断
DMA发送,把要发送的数据指针,字节数传入,即可自动发送。
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
这个函数实际是调用下面这个函数来启动发送的
HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t` DstAddress, uint32_t DataLength)
DMA接收,也是要指定接收的长度:
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
这个函数同样是调用
HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);
只不过源,目标 地址反过来
DMA还有暂停 、恢复函数 和 中止函数
5、freeRTOS+DMA+先进先出队列+动态内在申请
这个搞了几天,另个写。