RTOS UART

1.初始化qapi_uart结构体

uart数据结构分析
typedef struct
{
   uint32_t                     baud_Rate;  //波特率
   qapi_UART_Parity_Mode_e      parity_Mode; //奇偶校验方式
   qapi_UART_Num_Stop_Bits_e    num_Stop_Bits; //数据结束的bit位数
   qapi_UART_Bits_Per_Char_e    bits_Per_Char; //每个字符的位数
   qbool_t                      enable_Loopback; //回环功能
   qbool_t                      enable_Flow_Ctrl; //流量控制
   boolean                      user_mode_client; //预留驱动程序
   qapi_UART_Callback_Fn_t      tx_CB_ISR;       //tx回调
   qapi_UART_Callback_Fn_t      rx_CB_ISR;      //rx回调
}qapi_UART_Open_Config_t;

其中,qapi_UART_Open_Config_t结构体中,只有几个是我们需要关注的:

波特率、rx回调。其他的直接参考平台默认配置即可

    qapi_UART_Open_Config_t open_properties;
    qapi_Status_t q_status;

    memset(&open_properties, 0, sizeof(open_properties));

    open_properties.parity_Mode = QAPI_UART_NO_PARITY_E;
    open_properties.num_Stop_Bits= QAPI_UART_1_0_STOP_BITS_E;
    open_properties.baud_Rate   = 115200;
    open_properties.bits_Per_Char= QAPI_UART_8_BITS_PER_CHAR_E;

    open_properties.rx_CB_ISR = bt_rx_cb;
    open_properties.tx_CB_ISR = bt_tx_cb;

    open_properties.enable_Flow_Ctrl = false;
    open_properties.enable_Loopback = false;
    open_properties.user_mode_client = false;

2.初始化一个结构体,用于存放配置的uart handle

typedef struct UART_Ctx_s
{
    uint8_t buffer[BT_RX_BUFFER_SIZE + 128]; /* 串口接收的缓存Buffer */
    volatile uint32_t bufferFree;            /* 串口接收缓存剩余大小 */
    qapi_UART_Handle_t uartHandle;           /* 串口打开时的获取到的handle */
    qurt_signal_t event;                     /* 串口事件处理信号 */
    uint8_t uart_on_flg;                     /* 串口开关标志:1--开 0--关          */
} UART_Ctx_t;

3.将这个结构体注册到uat_port中

qapi_UART_Open(&UART_Ctx_D.uartHandle, g_port_id, &open_properties);

其中,g_port_id是平台使用的uart gpio组

4.底层qapi_uart进行uart的实际初始化

qapi_Status_t qapi_UART_Open
(
   qapi_UART_Handle_t*        handle,
   qapi_UART_Port_Id_e        id,
   qapi_UART_Open_Config_t*   config
)
{
    ...
    memsset(uart_h,0,sizeof(uart_handle));
    ...
    uart_get_device_props(id , &device_info)
    ...
}

qapi_UART_Open的前面一部分api都是判断这个handle以及gpio组是否已经被初始化过了,初始化中最关键的就是

memsset(uart_h,0,sizeof(uart_handle)); 将上面设置的handle赋值给uart_h

我们直接看下面

    init_Config.Ctxt               = uart_h;
    init_Config.enable_dma         = device_info.is_enable_dma;
    init_Config.uart_type          = UART_BAM;
    init_Config.uart_irq           = 0;  // Read from property file.  Don't override here.
    init_Config.rx_irq             = 0;  // Read from property file.  Don't override here.
    init_Config.LineEventNotif     =  NULL;//todo rajesh
    init_Config.TransmitReadyNotif =  (DALNotifyFunc)tx_cb_isr;
    init_Config.ReceiveReadyNotif  =  (DALNotifyFunc)rx_cb_isr;
    init_Config.CxmMisalignNotif   = NULL;
    init_Config.CxmMsgNotif        = NULL;
    init_Config.CxmRatNotif        = NULL;
    dal_device_handle = DalUart_Init(&init_Config);

对传入的handle、goio_id以及operation进行合法性校验后,放到UartInitConfig类型的init_config对象中进行uart dal注册,指定了对应的rx、rx回调。

初始化Init_config中主要做了以下事情:

static UART_CONTEXT *mu_init(UartInitConfig *init_cfg)
{
    ...
    if ( !get_properties(uart_ctxt) ) { goto error; }
    if ( !map_hardware(uart_ctxt)   ) { goto error; }
    if ( !create_objects(uart_ctxt) ) { goto error; }
    ...
    HALuart_InitObject(uart_hal, hal_type,
                      uart_ctxt->uart_mapping->virt_addr,
                      uart_ctxt->uart_mapping->phys_addr);
    ...
    HALuart_GetFIFOPhysAddrs(uart_hal, &dma_config.rx_fifo, &dma_config.tx_fifo);
    ...
    DalUart_Open(dal_device_handle, &open_config );
    ...
    (!(qurt_qapi_callback_configure(uart_h->pTxp->tx_cb_base, // type
                                                    (void *)0xDEADCAFE, // dummy obj_ptr
                                                    0,  // id
                                                    (void *)uart_h->pUartConfig->tx_CB_ISR, // test_api1 - a3: app_cb_dispatcher
                                                    (void *)pGlobUartTxCb, // test_api1 - a2: app_cb
                                                    (void**)&uart_h->pTxp->tx_cb_func,
                                                    NULL)))
    ...
    (!(qurt_qapi_callback_configure(uart_h->pRxp->rx_cb_base, // type
                                                    (void *)0xDEADCAFE, // dummy obj_ptr
                                                    0,  // id
                                                    (void *)uart_h->pUartConfig->rx_CB_ISR, // test_api1 - a3: app_cb_dispatcher
                                                    (void *)pGlobUartRxCb, // test_api1 - a2: app_cb
                                                    (void**)&uart_h->pRxp->rx_cb_func,
                                                    NULL)))
    ...

get_properties:从xml文件中获取dal层可配置的属性

map_hardware:映射物理地址到虚拟地址中

create_objects:创建uart对象,为下面初始化hal层的uart对象做准备

HALuart_GetFIFOPhysAddrs:返回dma交互所需要的rx、tx虚拟地址,上面已经绑定了rx、tx回调函数,这里拿到对应的虚拟地址

DalUart_Open:dal层uart初始化,这里很关键,下面会单独进行讲解

qurt_qapi_callback_configure:绑定uart的rx、tx回调

这里重点讲解以下DalUart_Open函数,其他的api基本都是平台与芯片高度绑定的,物理地址、虚拟地址之类的。我们一般不会对其进行修改。但是我们最上面的uart配置初始化基本是在这里进行处理的。

DalUart_Open实际上是调用mu_open(uart_ctxt, open_cfg)进行初始化的,uart_ctxt就是进行dal初始化的dal_device_handle,open_cfg就是Uart_Ctx_t.open_properties,初始化时的配置项。

static boolean mu_open(UART_CONTEXT *uart_ctxt, UartOpenConfig *open_cfg)
{
   void *id = uart_ctxt->init_cfg.device_id;
   boolean xfer_result;

   if ( !valid_char_format(&open_cfg->char_format) || !valid_rts_control(open_cfg->rts_control) )
   {
      tal_os_log(id, INFO, "mu_open: invalid parameter");
      return FALSE;
   }

   uart_ctxt->rts_control = open_cfg->rts_control;
   uart_ctxt->break_notified = FALSE;
   uart_ctxt->rx_overruns = 0;
   uart_ctxt->rx_cached_status = 0;
   uart_ctxt->rx_activated = FALSE;
   uart_ctxt->rx_halt = FALSE;
   uart_ctxt->tx_halt = FALSE;
   uart_ctxt->tx_wait = FALSE;
   uart_ctxt->break_halt = FALSE;
   
   if ( !allocate_buffers(uart_ctxt)     ) { goto error; }
   if ( !open_devices(uart_ctxt)         ) { goto error; }
   if ( !configure_interrupts(uart_ctxt) ) { goto error; }
   if ( !clock_enable(uart_ctxt)         ) { goto error; }

   if (uart_ctxt->use_dma)
   {
      if ( !dma_open(uart_ctxt->dma_ctxt) ) { goto error; }
   }

   configure_gsbi(uart_ctxt);

   if ( !gpio_enable(uart_ctxt)                   ) { goto error; }
   if ( !reg_init(uart_ctxt, open_cfg)            ) { goto error; }
   if ( !register_isr_for_uart(uart_ctxt)         ) { goto error; }
   uart_ctxt->power_state = POWER_FULL;

   xfer_result = rx_transfer_start(uart_ctxt);

   if (xfer_result == FALSE)
   {
      tal_os_log(id, ERROR, "mu_open: failed to start RX transfer");
      goto error;
   }

   set_rts_control(uart_ctxt, uart_ctxt->rts_control);
   start_break_detection(uart_ctxt);
   return TRUE;
error:
   mu_close(uart_ctxt);
   return FALSE;
}

valid_char_format:解析配置的奇偶校验位是否支持

valid_rts_control:解析配置的流量控制是否支持

allocate_buffers:配置rx、tx的buffer大小

open_devices:进行中断配置、gpio配置以及时钟配置

configure_interrupts:中断初始化,核心:uart_ctxt->rx_interrupt.irq = uart_ctxt->init_cfg.rx_irq;uart_ctxt->rx_interrupt.gpio = rx_gpio;

这里指定了rx的中断gpio脚

clock_enable:使能时钟

gpio_enable:使能gpio

reg_init:初始化uart寄存器

register_isr_for_uart:注册uart isr中断,高电平触发

rx_transfer_start:rx数据转换

到这里,uart初始化基本就完成了。

初始化结束后,uart是如何进行工作的呢?

还记得上面进行mu_open中初始化的各种参数吗?其中有一个register_isr_for_uart,用于注册uart的中断函数以及指定触发方式。触发中断后,会进入uart_dpc处理函数

static void *uart_dpc(void *context)
{
   UART_CONTEXT *uart_ctxt = (UART_CONTEXT *)context;
   void *id = uart_ctxt->init_cfg.device_id;
   uint32 uart_events;
   HALUartObject *uart_hal = &uart_ctxt->uart_hal;
   uint32 isr_back,isr;
   ...
   dbg_restore_uart_event(&uart_events, id);
   ...
   if (uart_events & IRQ_RXSTALE)       { service_rx_stale      (uart_ctxt); }
   if (uart_events & IRQ_RXLEV)         { service_rx_watermark  (uart_ctxt); }
   if (uart_events & IRQ_TXLEV)         { service_tx_watermark  (uart_ctxt); }
   if (uart_events & IRQ_TX_READY)      { service_tx_ready      (uart_ctxt); }
   if (uart_events & IRQ_RXBREAK_START) { service_rx_break_start(uart_ctxt); }
   if (uart_events & DMA_RX_COMPLETE)   { service_rx_dma        (uart_ctxt); }
   if (uart_events & DMA_TX_COMPLETE)   { service_tx_dma        (uart_ctxt); }
   ...
 }

在dpc中,通过uart_events去接收uart中断事件,当中断事件为IRQ_RXSTALE,即uart rx事件,调用service_rx_stale,再判断当前uart的rx是否已经接受完整数据,如果接收完整,再通过notify_rxdata_available进入rx_cb_isr进行下一步调用

那么rx_cb_isr与open_properties.rx_CB_ISR = bt_rx_cb;的关系是什么呢?

static void rx_cb_isr(uart_handle *uart_h)
{
  cb_info *pRxNode      = NULL;
  uint8   *pBuf         = NULL;
  DALResult result      = UART_ERROR;
  uint32   bytes_read   = 0;
  uint32   read_size    = 0;
  uint32 overrun_events = 0;
  uint32 cb_idx = 0;
  UartStatusType status = (UartStatusType)0;

  if(uart_h == NULL)
  {
          UART_LOG_0( ERROR, "rx_cb_isr:null handle" );
          return;
  }
  qurt_mutex_lock(uart_h->pRxp->pLock);

  pRxNode = malloc(sizeof(cb_info));
  if(pRxNode == NULL)
  {
          UART_LOG_0( TRACE, "[%d] pRxNode:memory alloc failed" );
          return;
  }
  memsset(pRxNode,0,sizeof(cb_info));


  while(TRUE == PopHeadNode(&uart_h->pRxp->pRxHead, pRxNode))
  {
          pBuf = (uint8 *)pRxNode->pBuf;
          read_size = pRxNode->size;
          result = DalUart_Read(uart_h->pDeviceHandle ,pBuf,
                          read_size,&bytes_read, &overrun_events);
          if(result == UART_SUCCESS)
          {
                  UART_BUFFER_LOG_2( pBuf, bytes_read, "[%d] Recived %d bytes:", 0, bytes_read );
                  UART_LOG_1(ERROR, "Recived %d bytes:", bytes_read );
                  pRxNode->size = bytes_read;
                  if(uart_h->pUartConfig->user_mode_client)
                  {
                          uart_h->pRxp->rx_cb_func(pRxNode->size, (UINT)pRxNode->pCbData);
                          cb_idx                    = uart_h->pRxp->rx_cb_base - TXM_QAPI_BUSES_UART_CB_BASE;
                          cb_base_array[cb_idx]     = CB_BASE_AVAILABLE;
                  }
                  else
                  {
                          if(uart_h->pUartConfig->rx_CB_ISR != NULL)
                          {
                                  uart_h->pUartConfig->rx_CB_ISR(pRxNode->size,pRxNode->pCbData);
                          }
                  }
                  result = DalUart_GetStatus(uart_h->pDeviceHandle,&status);
                  if((result == UART_SUCCESS) && (status & STATUS_RX_BUFFER_EMPTY))
                  {
                          break;
                  }
                  bytes_read = 0;
          }
  }
  free(pRxNode);
  pRxNode = NULL;

  qurt_mutex_unlock(uart_h->pRxp->pLock);
}

这里可以看到,触发了rx_cb_isr后,首先会上个锁qurt_mutex_lock,避免正在处理这一条消息时,又再次触发中断导致数据异常

随后进入在while循环内,通过DalUart_Read对uart数据进行读取,判断uart config配置中是否已经设置rx_CB_ISR回调函数,设置了就会将uart读到的数据长度、数据指针传给uart config中的rx_CB_ISR进行数据解析。

并且,rx_cb_isr会一直等待,直到DalUart_GetStatus中判定uart rx buffer此时为空,即rx_CB_ISR已经将数据全部处理完并清空buffer后,才会进行解锁,等待下一个uart中断。

rx_CB_ISR中的需要实现的就是:

对传入的length、data数据进行解析并处理即可。但是由于底层存在锁,所以耗时的操作不建议放置在rx_CB_ISR中执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值