接上篇有了uart設備對象及rt_uart_ops結構體以后,我們就需要提供對應的真實的能操控硬件的函數,其實真正操作的硬件的函數并不多,創建的結構體對象如下:
static const struct rt_uart_ops stm32_uart_ops =
{
.configure = stm32_configure,
.control = stm32_control,
.putc = stm32_putc,
.getc = stm32_getc,
.dma_transmit = stm32_dma_transmit
};
先來看下基于hal庫代碼實現的stm32_configure的完整代碼,當你需要更換其它單片機時,再系統沒有提供對應的bsp的情況下,你可以仿照代碼進行重構該函數用于適用于特殊的硬件,系統也提供了很多種不同的芯片的bsp:
static rt_err_t stm32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
struct stm32_uart *uart;
RT_ASSERT(serial != RT_NULL);
RT_ASSERT(cfg != RT_NULL);
uart = rt_container_of(serial, struct stm32_uart, serial);
uart->handle.Instance = uart->config->Instance;
uart->handle.Init.BaudRate = cfg->baud_rate;
uart->handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
uart->handle.Init.Mode = UART_MODE_TX_RX;
uart->handle.Init.OverSampling = UART_OVERSAMPLING_16;
switch (cfg->data_bits)
{
case DATA_BITS_8:
uart->handle.Init.WordLength = UART_WORDLENGTH_8B;
break;
case DATA_BITS_9:
uart->handle.Init.WordLength = UART_WORDLENGTH_9B;
break;
default:
uart->handle.Init.WordLength = UART_WORDLENGTH_8B;
break;
}
switch (cfg->stop_bits)
{
case STOP_BITS_1:
uart->handle.Init.StopBits = UART_STOPBITS_1;
break;
case STOP_BITS_2:
uart->handle.Init.StopBits = UART_STOPBITS_2;
break;
default:
uart->handle.Init.StopBits = UART_STOPBITS_1;
break;
}
switch (cfg->parity)
{
case PARITY_NONE:
uart->handle.Init.Parity = UART_PARITY_NONE;
break;
case PARITY_ODD:
uart->handle.Init.Parity = UART_PARITY_ODD;
break;
case PARITY_EVEN:
uart->handle.Init.Parity = UART_PARITY_EVEN;
break;
default:
uart->handle.Init.Parity = UART_PARITY_NONE;
break;
}
#ifdef RT_SERIAL_USING_DMA
uart->dma_rx.last_index = 0;
#endif
if (HAL_UART_Init(&uart->handle) != HAL_OK)
{
return -RT_ERROR;
}
return RT_EOK;
}
代碼很長,但是功能很簡單就是對串口進行初始化,設置串口波特率,硬件流控,串口模式、串口采樣、數據位、停止位、DMA相關參數、最后調用HAL_UART_Init()完成串口的初始化功能。
接下來看下stm32_control函數的代碼,代碼雖長單功能并不復雜:
static rt_err_t stm32_control(struct rt_serial_device *serial, int cmd, void *arg)
{
struct stm32_uart *uart;
#ifdef RT_SERIAL_USING_DMA
rt_ubase_t ctrl_arg = (rt_ubase_t)arg;
#endif
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct stm32_uart, serial);
switch (cmd)
{
/* disable interrupt */
case RT_DEVICE_CTRL_CLR_INT:
/* disable rx irq */
NVIC_DisableIRQ(uart->config->irq_type);
/* disable interrupt */
__HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_RXNE);
#ifdef RT_SERIAL_USING_DMA
/* disable DMA */
if (ctrl_arg == RT_DEVICE_FLAG_DMA_RX)
{
HAL_NVIC_DisableIRQ(uart->config->dma_rx->dma_irq);
if (HAL_DMA_Abort(&(uart->dma_rx.handle)) != HAL_OK)
{
RT_ASSERT(0);
}
if (HAL_DMA_DeInit(&(uart->dma_rx.handle)) != HAL_OK)
{
RT_ASSERT(0);
}
}
else if(ctrl_arg == RT_DEVICE_FLAG_DMA_TX)
{
HAL_NVIC_DisableIRQ(uart->config->dma_tx->dma_irq);
if (HAL_DMA_DeInit(&(uart->dma_tx.handle)) != HAL_OK)
{
RT_ASSERT(0);
}
}
#endif
break;
/* enable interrupt */
case RT_DEVICE_CTRL_SET_INT:
/* enable rx irq */
HAL_NVIC_SetPriority(uart->config->irq_type, 1, 0);
HAL_NVIC_EnableIRQ(uart->config->irq_type);
/* enable interrupt */
__HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_RXNE);
break;
#ifdef RT_SERIAL_USING_DMA
case RT_DEVICE_CTRL_CONFIG:
stm32_dma_config(serial, ctrl_arg);
break;
#endif
case RT_DEVICE_CTRL_CLOSE:
if (HAL_UART_DeInit(&(uart->handle)) != HAL_OK )
{
RT_ASSERT(0)
}
break;
}
return RT_EOK;
}
主要看一下幾個控制參數:RT_DEVICE_CTRL_CLR_INT關閉中斷,如果有使用dma則關閉DMA。RT_DEVICE_CTRL_SET_INT:開啟終端,如果有使用dma則采用dma_config功能打開dma。RT_DEVICE_CTRL_CLOSE:關閉串口,就這幾個功能。
發送函數功能最簡單,輕松TC中斷標記,將數據放入DR數據寄存器中,while循環等待TC中斷觸發(DR數據寄存器為空):
static int stm32_putc(struct rt_serial_device *serial, char c)
{
struct stm32_uart *uart;
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct stm32_uart, serial);
UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC);
#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \
|| defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \
|| defined(SOC_SERIES_STM32G4)
uart->handle.Instance->TDR = c;
#else
uart->handle.Instance->DR = c;
#endif
while (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) == RESET);
return 1;
}
接下來是讀取數據,這里有個有意思的現象,看過源碼你會發現讀取數據和寫入數據訪問的實際的硬件寄存器都是一樣的,DR or RDR,那么你會不會有個疑問,那么某一刻讀取的數據會不會是剛寫入的數據,代碼之后揭曉:
static int stm32_getc(struct rt_serial_device *serial)
{
int ch;
struct stm32_uart *uart;
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct stm32_uart, serial);
ch = -1;
if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET)
{
#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \
|| defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \
|| defined(SOC_SERIES_STM32G4)
ch = uart->handle.Instance->RDR & 0xff;
#else
ch = uart->handle.Instance->DR & 0xff;
#endif
}
return ch;
}
揭曉答案啦:這里以DR為例,其實它是一對神奇的雙胞胎,一個名字對應著兩個真實的寄存器,當你寫入數據時實際寫入的是發送DR,當你讀取數據時實際訪問的是接收DR,是通過你對硬件的讀or寫操作,硬件自行對應的,當年在這個問題上耗死了好些的腦細胞,也不知道是哪個天才設計的機制。
最后到了還有連續傳送字節(發送)的實現,借助的硬件的DMA技術實現的:
static rt_size_t stm32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction)
{
struct stm32_uart *uart;
RT_ASSERT(serial != RT_NULL);
RT_ASSERT(buf != RT_NULL);
uart = rt_container_of(serial, struct stm32_uart, serial);
if (size == 0)
{
return 0;
}
if (RT_SERIAL_DMA_TX == direction)
{
if (HAL_UART_Transmit_DMA(&uart->handle, buf, size) == HAL_OK)
{
return size;
}
else
{
return 0;
}
}
return 0;
}
帶著問題結束本篇吧,發送很簡單 一個接一個的發送出去,那么接收呢,DR中有沒有數據,是不是需要頻繁的觸發讀取函數,或者讀不及時會不會被下一個來的數據將DR中的數據覆蓋,或者數據直接被丟掉呢?