????????rtt标准版中stm32的bsp使用的是HAL库,现在想换成标准库。相比于rtt nano的移植来说,和标准版的移植还是有不小的差别的,至少我个人这么认为。nano在移植过程中只需要按照#error提示修改好board.c即可。当然如果需要使用finsh的话,你还需要补充完成finsh_board.c和串口的一些信号,具体参见官方文档。整个流程还是相当清晰的,工作量也不大。
????????移植标准版的另一个原因是我想使用AT?Client。刚开始我尝试直接向nano版本中进行添加,但似乎nano中不少文件都进行了精简,无奈之下只得重新对rtt进行移植。其实HAL库替换成标准库并不复杂,只是稍微有些麻烦而已。详细的移植过程由于之前没保存就不细写了,下文就finsh移植过程中串口的一些问题做一些记录。此次移植中串口暂不包含DMA。keil文件结构如下:
?????????在nano版本中,Systick等的适配工作主要在board.c中完成,但在标准版中这部分需要在drv_common.c中处理,大致内容与nano版本差不多:
?????????对于串口而言,基于HAL库的代码主要在drv_usart.c中对串口的读写控制进行编写,同时完成串口设备向系统的注册。这一部分要修改的内容需要注意,因为HAL库的一些结构体与标准库不同,因此需要按照标准库的要求进行替换和修改,这些工作基本都在drv_usart.h中完成。
????????需要注意的是新增的handleInstance元素,它用来表明是哪个串口。对于HAL库而言,似乎这个库的handle中以及有类似的元素存在,因此直接存到handle中了。但标准库的USART_InitTypeDef结构体中尚不支持,所以手动添加这样一个元素,以便后边使用。接着修改drv_usart.c文件,在我的工程中对各个串口的初始化单独写到了USART.c中。因此,串口初始化函数修改后如下:
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->handleInstance = uart->config->Instance;
uart->handle.USART_BaudRate = cfg->baud_rate;
uart->handle.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
uart->handle.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
// uart->handle.Init.OverSampling = UART_OVERSAMPLING_16;
switch (cfg->data_bits)
{
case DATA_BITS_8:
uart->handle.USART_WordLength = USART_WordLength_8b;
break;
case DATA_BITS_9:
uart->handle.USART_WordLength = USART_WordLength_9b;
break;
default:
uart->handle.USART_WordLength = USART_WordLength_8b;
break;
}
switch (cfg->stop_bits)
{
case STOP_BITS_1:
uart->handle.USART_StopBits = USART_StopBits_1;
break;
case STOP_BITS_2:
uart->handle.USART_StopBits = USART_StopBits_2;
break;
default:
uart->handle.USART_StopBits = USART_StopBits_1;
break;
}
switch (cfg->parity)
{
case PARITY_NONE:
uart->handle.USART_Parity = USART_Parity_No;
break;
case PARITY_ODD:
uart->handle.USART_Parity = USART_Parity_Odd;
break;
case PARITY_EVEN:
uart->handle.USART_Parity = USART_Parity_Even;
break;
default:
uart->handle.USART_Parity = USART_Parity_No;
break;
}
#ifdef RT_SERIAL_USING_DMA
uart->dma_rx.last_index = 0;
#endif
// if (HAL_UART_Init(&uart->handle) != HAL_OK)
// {
// return -RT_ERROR;
// }
#ifdef BSP_USING_UART
if (uart->handleInstance == USART1) {
USART1_Init(uart->handle.USART_BaudRate);
}
if (uart->handleInstance == USART2) {
USART_DeInit(uart->handleInstance);
USART2_Init(uart->handle.USART_BaudRate);
}
if (uart->handleInstance == USART3) {
USART_DeInit(uart->handleInstance);
USART3_Init(uart->handle.USART_BaudRate);
}
if (uart->handleInstance == UART4) {
USART_DeInit(uart->handleInstance);
UART4_Init(uart->handle.USART_BaudRate);
}
#endif
return RT_EOK;
}
????????工程中由于只用4个串口,因此没有对芯片所有的串口资源都做处理。接着,照猫画虎修改控制函数。这个函数我并没有做完整的处理,后面再慢慢修改吧
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 */
// rt_kprintf("stop uart irq\n");
// 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 */
// NVIC_EnableIRQ(uart->config->irq_type);
// rt_kprintf("allow uart irq\n");
// 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:
// rt_kprintf("reset uart\n");
USART_DeInit(uart->handleInstance);
break;
}
return RT_EOK;
}
修改stm32_putc和stm32_getc:
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);
#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
USART_SendData(uart->handleInstance, c);
while (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_TXE) == RESET);
while (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_TC) == RESET);
#endif
return 1;
}
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 (USART_GetITStatus(uart->handleInstance, USART_IT_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->config->Instance->RDR & 0xff;
#else
ch = uart->config->Instance->DR & 0xff;
#endif
}
return ch;
}
修改中断处理:
/**
* Uart common interrupt process. This need add to uart ISR.
*
* @param serial serial device
*/
static void uart_isr(struct rt_serial_device *serial)
{
struct stm32_uart *uart;
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct stm32_uart, serial);
/* UART in mode Receiver -------------------------------------------------*/
if ((USART_GetFlagStatus(uart->handleInstance, USART_FLAG_RXNE) != RESET) &&
(USART_GetITStatus(uart->handleInstance, USART_IT_RXNE) != RESET))
{
rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
}
else
{
// if (USART_GetFlagStatus(uart->handleInstance, UART_FLAG_ORE) != RESET)
// {
// __HAL_UART_CLEAR_OREFLAG(&uart->handle);
// }
if (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_NE) != RESET)
{
USART_ClearFlag(uart->handleInstance, USART_FLAG_NE);
}
if (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_FE) != RESET)
{
USART_ClearFlag(uart->handleInstance, USART_FLAG_FE);
}
if (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_PE) != RESET)
{
USART_ClearFlag(uart->handleInstance, USART_FLAG_PE);
}
#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)
if (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_LBD) != RESET)
{
USART_ClearFlag(uart->handleInstance, USART_FLAG_LBD);
}
#endif
if (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_CTS) != RESET)
{
USART_ClearFlag(uart->handleInstance, USART_FLAG_CTS);
}
if (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_TXE) != RESET)
{
USART_ClearFlag(uart->handleInstance, USART_FLAG_TXE);
}
if (USART_GetFlagStatus(uart->handleInstance, USART_FLAG_TC) != RESET)
{
USART_ClearFlag(uart->handleInstance, USART_FLAG_TC);
}
if (USART_GetITStatus(uart->handleInstance, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(uart->handleInstance, USART_IT_RXNE); // 清中断标志
}
}
}
????????uart_isr函数是我个人觉得需要注意的另一处重点。最早开始移植的时候从putty中能看到输出的启动信息,但是无论怎么按键盘rtt控制台都始终没有反应。点灯大法显示串口确实进接收中断了,输出的话rt_kprintf一点毛病都没有,就是输入不行!
官方finsh移植说明是这么写的:
?然后,在rt-thread-3.1.5\src\device.c中找到了rt_device_read函数。。。实现好了
rx_indicate函数没找到,但在rt-thread-3.1.5\components\finsh\shell.c中找到个类似的:
?????????我是真的迷茫了
一气之下套娃实现了一个?rx_indicate()然后在串口1的中断函数中进行调用,结果还是不行。。。
????????细看rt-thread-3.1.5\components\finsh\shell.c的代码发现实际上官方文档中说的rx_indicate()十有八九就是上图中finsh_rx_ind这个函数,然后在finsh的初始化中已经对这个函数做了绑定。只要有触发的信号系统会自动调用此回调函数:
这文档是在玩我吗。。。
????????无奈之下 再次打开HAL库的工程模板,此模板中对串口的中断做了统一的处理,也就是通过uart_isr函数对串口的相关标志位进行清除,然后通过rt_hw_serial_isr函数对系统进行通知。之后系统便会通过stm32_getc来获取输入。所以,重点便是rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
这大坑踩得是真酸爽
往后基本就没什么修改的了,USART中断统一的制式:
void USART1_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter(); //在中断中一定要调用这对函数,进入中断
uart_isr(&(uart_obj[UART1_INDEX].serial));
/* leave interrupt */
rt_interrupt_leave(); //在中断中一定要调用这对函数,离开中断
}
void USART2_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter(); //在中断中一定要调用这对函数,进入中断
uart_isr(&(uart_obj[UART2_INDEX].serial));
/* leave interrupt */
rt_interrupt_leave(); //在中断中一定要调用这对函数,离开中断
}
......
编译、烧写,Finsh总算搞定了:
?下一步准备AT?Client,希望一切顺利。。。
|