bluekitchen-stm32f1/csr8311移植笔记(3)-hci driver & hci log driver

bluekitchen文件树结构:
在这里插入图片描述
可以看出:
1)chipset下是bluekitchen目前所支持的所有bt chip集合
2)platform下是可供选择的跑bluekitchen协议栈的软件平台,比如embedded(即裸机系统),freertos,posix,windows等
3)example下是各种profile和service demo

我们在port下添加stm32f103zet6_csr8x11例程。这样platform对应embedded,chipset对应csr,我们需要做的是实现hci driver,hci log driver以及用来管理连接设备信息的flash driver。
在这里插入图片描述
platform->embedded
在这里插入图片描述
btstack_port.c中的几个关键函数:
(1)实现gcc下printf的底层io函数_write

int _write(int file, char *ptr, int len){
#if 1
    uint8_t cr = '\r';
    int i;

    if (file == STDOUT_FILENO || file == STDERR_FILENO) {
#if 1
        for (i = 0; i < len; i++) {
            if (ptr[i] == '\n') {
                HAL_UART_Transmit( &huart1, &cr, 1, HAL_MAX_DELAY );
            }
            HAL_UART_Transmit( &huart1, (uint8_t *) &ptr[i], 1, HAL_MAX_DELAY );
        }
#endif
        return i;
    }
    errno = EIO;
    return -1;
#else
    return len;
#endif
}

(2)实现HCI driver (UART2) TX & RX callback的设置接口

//stm32 UART2 hal tx complete callback,tx完成时调用,这个函数会替代stm32 hal中的同名weak symbol
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
    if (huart == &huart2){
        (*tx_done_handler)();
    }
}
//stm32 UART2 hal rx complete callback,rx完成时调用,这个函数会替代stm32 hal中的同名weak symbol
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
    if (huart == &huart2){
        (*rx_done_handler)();
    }
}
//设置rx callback
void hal_uart_dma_set_block_received( void (*the_block_handler)(void)){
    rx_done_handler = the_block_handler;
}
//设置tx callback
void hal_uart_dma_set_block_sent( void (*the_block_handler)(void)){
    tx_done_handler = the_block_handler;
}

(3)实现HCI driver TX & RX函数,以及HCI log driver

void hal_uart_dma_send_block(const uint8_t *data, uint16_t size){
    HAL_UART_Transmit_DMA( &huart2, (uint8_t *) data, size);
}

void hal_uart_dma_receive_block(uint8_t *data, uint16_t size){
    HAL_UART_Receive_DMA( &huart2, data, size );
}

void hal_uart_dma_send_block_for_hci(const uint8_t *data, uint16_t size){
    HAL_UART_Transmit( &huart3, (uint8_t *) data, size, HAL_MAX_DELAY );
}

bluekitchen TX & RX数据流大致如下:
在这里插入图片描述
(4)为了简化问题,我们先不实现flash driver,内容全部放空
(5)最后是协议栈的配置:

void port_main(void){
    // start with BTstack init - especially configure HCI Transport
    btstack_memory_init();
    btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
    // uncomment to enable packet logger
#ifdef ENABLE_SEGGER_RTT
    // hci_dump_init(hci_dump_segger_rtt_stdout_get_instance());
#else
    hci_dump_init(hci_dump_embedded_stdout_get_instance());
#endif
    // init HCI
    hci_init(hci_transport_h4_instance(btstack_uart_block_embedded_instance()), (void*) &config);
    hci_set_chipset(btstack_chipset_csr_instance());
#if 0
    // setup TLV Flash Sector implementation
    const hal_flash_bank_t * hal_flash_bank_impl = hal_flash_bank_stm32_init_instance(
            &hal_flash_bank_context,
            HAL_FLASH_BANK_SIZE,
            HAL_FLASH_BANK_0_SECTOR,
            HAL_FLASH_BANK_1_SECTOR,
            HAL_FLASH_BANK_0_ADDR,
            HAL_FLASH_BANK_1_ADDR);
    const btstack_tlv_t * btstack_tlv_impl = btstack_tlv_flash_bank_init_instance(
            &btstack_tlv_flash_bank_context,
            hal_flash_bank_impl,
            &hal_flash_bank_context);
    // setup global tlv
    btstack_tlv_set_instance(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
    // setup Link Key DB using TLV
    const btstack_link_key_db_t * btstack_link_key_db = btstack_link_key_db_tlv_get_instance(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
    hci_set_link_key_db(btstack_link_key_db);
    // setup LE Device DB using TLV
    le_device_db_tlv_configure(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
#endif
#ifdef HAVE_HAL_AUDIO
    // setup audio
    btstack_audio_sink_set_instance(btstack_audio_embedded_sink_get_instance());
    btstack_audio_source_set_instance(btstack_audio_embedded_source_get_instance());
#endif
    // inform about BTstack state
    hci_event_callback_registration.callback = &packet_handler;
    hci_add_event_handler(&hci_event_callback_registration);
    // hand over to btstack embedded code
    btstack_main(0, NULL);
    // go
    btstack_run_loop_execute();
}

编译工程后开始调试,记录下遇到的几个问题。

Q1:controller有上报event,但是格式不正确:(开机后会重新调整波特率,导致波特率不匹配)
在这里插入图片描述
首先需要解决2个问题:
1)system log重叠和丢失的问题 <uart1 tx & rx开DMA>
2)hci logging tool crash问题(看现象似乎是波特率太快,tool收不过来)
已解决:波特率太高,丢包,导致logging tool内存越界

Q2:似乎uart1 & uart2 DMA通道有冲突,使用uart1 DMA来传输log时,uart2无法正确收controller event?
暂无法解释。简化问题,只开uart2 RX & TX DMA。

Q3:在下完hci_reset_cm后,下read_local_version_cmd,无法收到正确的hci_evt,流程中断?
奇怪的现象,关闭uart1 hci dump后,就正常了。
更进一步,在如下函数中只打印message type就可以正常接收controller event:
太诡异了,无法理解!!!!!!
会不会是因为在UART2 RX ISR中调用printf???
在这里插入图片描述
可以排除这个猜想。

Q4:将波特率改为921600后,无法工作,收不到event。
暂未解决。

Q5:开启可见性后,使用手机去连接设备,会遇到event | acl解析错误:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对比正常的log:(红框中的就是丢失数据的hci event)
在这里插入图片描述
从发送完Read_Remote_Supported_Features cmd开始,event packet就丢失偶数次序的byte?

问题的原因终于找到了,在bluekitchen原始仓库的closed issue中,发现有人遇到了同样的问题,参考bluekitchen团队写的开发blog:https://bluekitchen-gmbh.com/btstack-port-for-stm32-f4-discovery-board-with-cc256x/
发现是UART2 RX DMA的使用上出了问题,bluekitchen hci_transport_h4.c 读数据的策略是分批次读取,1)hci_packet_type, 2)packet_header, 3)payload,在1)和2)之间的数据会丢失:

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }
    /* Process Locked */
    __HAL_LOCK(huart);
    /* Set Reception type to Standard reception */
    huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;

    return(UART_Start_Receive_DMA(huart, pData, Size));
  }
  else
  {
    return HAL_BUSY;
  }
}


HAL_StatusTypeDef UART_Start_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  uint32_t *tmp;

  huart->pRxBuffPtr = pData;
  huart->RxXferSize = Size;

  huart->ErrorCode = HAL_UART_ERROR_NONE;
  huart->RxState = HAL_UART_STATE_BUSY_RX;

  /* Set the UART DMA transfer complete callback */
  huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt;

  /* Set the UART DMA Half transfer complete callback */
  huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;

  /* Set the DMA error callback */
  huart->hdmarx->XferErrorCallback = UART_DMAError;

  /* Set the DMA abort callback */
  huart->hdmarx->XferAbortCallback = NULL;

  /* Enable the DMA stream */
  tmp = (uint32_t *)&pData;
  HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t *)tmp, Size);

  /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */
  //__HAL_UART_CLEAR_OREFLAG(huart);

  /* Process Unlocked */
  __HAL_UNLOCK(huart);

  /* Enable the UART Parity Error Interrupt */
  SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);

  /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  SET_BIT(huart->Instance->CR3, USART_CR3_EIE);

  /* Enable the DMA transfer for the receiver request by setting the DMAR bit
  in the UART CR3 register */
  SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);

  return HAL_OK;
}


#define __HAL_UART_CLEAR_OREFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)


#define __HAL_UART_CLEAR_PEFLAG(__HANDLE__)     \
  do{                                           \
    __IO uint32_t tmpreg = 0x00U;               \
    tmpreg = (__HANDLE__)->Instance->SR;        \
    tmpreg = (__HANDLE__)->Instance->DR;        \
    UNUSED(tmpreg);                             \
  } while(0U)

__HAL_UART_CLEAR_OREFLAG(HANDLE)

这个宏会读uart状态寄存器和数据寄存器,当然会丢失数据。。。。。
屏蔽掉这段代码一切问题都解决了。

版权声明:本文为CSDN博主「熟能生巧啊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38177846/article/details/122573119

bluekitchen文件树结构:
在这里插入图片描述
可以看出:
1)chipset下是bluekitchen目前所支持的所有bt chip集合
2)platform下是可供选择的跑bluekitchen协议栈的软件平台,比如embedded(即裸机系统),freertos,posix,windows等
3)example下是各种profile和service demo

我们在port下添加stm32f103zet6_csr8x11例程。这样platform对应embedded,chipset对应csr,我们需要做的是实现hci driver,hci log driver以及用来管理连接设备信息的flash driver。
在这里插入图片描述
platform->embedded
在这里插入图片描述
btstack_port.c中的几个关键函数:
(1)实现gcc下printf的底层io函数_write

int _write(int file, char *ptr, int len){
#if 1
    uint8_t cr = '\r';
    int i;

    if (file == STDOUT_FILENO || file == STDERR_FILENO) {
#if 1
        for (i = 0; i < len; i++) {
            if (ptr[i] == '\n') {
                HAL_UART_Transmit( &huart1, &cr, 1, HAL_MAX_DELAY );
            }
            HAL_UART_Transmit( &huart1, (uint8_t *) &ptr[i], 1, HAL_MAX_DELAY );
        }
#endif
        return i;
    }
    errno = EIO;
    return -1;
#else
    return len;
#endif
}

(2)实现HCI driver (UART2) TX & RX callback的设置接口

//stm32 UART2 hal tx complete callback,tx完成时调用,这个函数会替代stm32 hal中的同名weak symbol
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
    if (huart == &huart2){
        (*tx_done_handler)();
    }
}
//stm32 UART2 hal rx complete callback,rx完成时调用,这个函数会替代stm32 hal中的同名weak symbol
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
    if (huart == &huart2){
        (*rx_done_handler)();
    }
}
//设置rx callback
void hal_uart_dma_set_block_received( void (*the_block_handler)(void)){
    rx_done_handler = the_block_handler;
}
//设置tx callback
void hal_uart_dma_set_block_sent( void (*the_block_handler)(void)){
    tx_done_handler = the_block_handler;
}

(3)实现HCI driver TX & RX函数,以及HCI log driver

void hal_uart_dma_send_block(const uint8_t *data, uint16_t size){
    HAL_UART_Transmit_DMA( &huart2, (uint8_t *) data, size);
}

void hal_uart_dma_receive_block(uint8_t *data, uint16_t size){
    HAL_UART_Receive_DMA( &huart2, data, size );
}

void hal_uart_dma_send_block_for_hci(const uint8_t *data, uint16_t size){
    HAL_UART_Transmit( &huart3, (uint8_t *) data, size, HAL_MAX_DELAY );
}

bluekitchen TX & RX数据流大致如下:
在这里插入图片描述
(4)为了简化问题,我们先不实现flash driver,内容全部放空
(5)最后是协议栈的配置:

void port_main(void){
    // start with BTstack init - especially configure HCI Transport
    btstack_memory_init();
    btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
    // uncomment to enable packet logger
#ifdef ENABLE_SEGGER_RTT
    // hci_dump_init(hci_dump_segger_rtt_stdout_get_instance());
#else
    hci_dump_init(hci_dump_embedded_stdout_get_instance());
#endif
    // init HCI
    hci_init(hci_transport_h4_instance(btstack_uart_block_embedded_instance()), (void*) &config);
    hci_set_chipset(btstack_chipset_csr_instance());
#if 0
    // setup TLV Flash Sector implementation
    const hal_flash_bank_t * hal_flash_bank_impl = hal_flash_bank_stm32_init_instance(
            &hal_flash_bank_context,
            HAL_FLASH_BANK_SIZE,
            HAL_FLASH_BANK_0_SECTOR,
            HAL_FLASH_BANK_1_SECTOR,
            HAL_FLASH_BANK_0_ADDR,
            HAL_FLASH_BANK_1_ADDR);
    const btstack_tlv_t * btstack_tlv_impl = btstack_tlv_flash_bank_init_instance(
            &btstack_tlv_flash_bank_context,
            hal_flash_bank_impl,
            &hal_flash_bank_context);
    // setup global tlv
    btstack_tlv_set_instance(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
    // setup Link Key DB using TLV
    const btstack_link_key_db_t * btstack_link_key_db = btstack_link_key_db_tlv_get_instance(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
    hci_set_link_key_db(btstack_link_key_db);
    // setup LE Device DB using TLV
    le_device_db_tlv_configure(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
#endif
#ifdef HAVE_HAL_AUDIO
    // setup audio
    btstack_audio_sink_set_instance(btstack_audio_embedded_sink_get_instance());
    btstack_audio_source_set_instance(btstack_audio_embedded_source_get_instance());
#endif
    // inform about BTstack state
    hci_event_callback_registration.callback = &packet_handler;
    hci_add_event_handler(&hci_event_callback_registration);
    // hand over to btstack embedded code
    btstack_main(0, NULL);
    // go
    btstack_run_loop_execute();
}

编译工程后开始调试,记录下遇到的几个问题。

Q1:controller有上报event,但是格式不正确:(开机后会重新调整波特率,导致波特率不匹配)
在这里插入图片描述
首先需要解决2个问题:
1)system log重叠和丢失的问题 <uart1 tx & rx开DMA>
2)hci logging tool crash问题(看现象似乎是波特率太快,tool收不过来)
已解决:波特率太高,丢包,导致logging tool内存越界

Q2:似乎uart1 & uart2 DMA通道有冲突,使用uart1 DMA来传输log时,uart2无法正确收controller event?
暂无法解释。简化问题,只开uart2 RX & TX DMA。

Q3:在下完hci_reset_cm后,下read_local_version_cmd,无法收到正确的hci_evt,流程中断?
奇怪的现象,关闭uart1 hci dump后,就正常了。
更进一步,在如下函数中只打印message type就可以正常接收controller event:
太诡异了,无法理解!!!!!!
会不会是因为在UART2 RX ISR中调用printf???
在这里插入图片描述
可以排除这个猜想。

Q4:将波特率改为921600后,无法工作,收不到event。
暂未解决。

Q5:开启可见性后,使用手机去连接设备,会遇到event | acl解析错误:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对比正常的log:(红框中的就是丢失数据的hci event)
在这里插入图片描述
从发送完Read_Remote_Supported_Features cmd开始,event packet就丢失偶数次序的byte?

问题的原因终于找到了,在bluekitchen原始仓库的closed issue中,发现有人遇到了同样的问题,参考bluekitchen团队写的开发blog:https://bluekitchen-gmbh.com/btstack-port-for-stm32-f4-discovery-board-with-cc256x/
发现是UART2 RX DMA的使用上出了问题,bluekitchen hci_transport_h4.c 读数据的策略是分批次读取,1)hci_packet_type, 2)packet_header, 3)payload,在1)和2)之间的数据会丢失:

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }
    /* Process Locked */
    __HAL_LOCK(huart);
    /* Set Reception type to Standard reception */
    huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;

    return(UART_Start_Receive_DMA(huart, pData, Size));
  }
  else
  {
    return HAL_BUSY;
  }
}


HAL_StatusTypeDef UART_Start_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  uint32_t *tmp;

  huart->pRxBuffPtr = pData;
  huart->RxXferSize = Size;

  huart->ErrorCode = HAL_UART_ERROR_NONE;
  huart->RxState = HAL_UART_STATE_BUSY_RX;

  /* Set the UART DMA transfer complete callback */
  huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt;

  /* Set the UART DMA Half transfer complete callback */
  huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;

  /* Set the DMA error callback */
  huart->hdmarx->XferErrorCallback = UART_DMAError;

  /* Set the DMA abort callback */
  huart->hdmarx->XferAbortCallback = NULL;

  /* Enable the DMA stream */
  tmp = (uint32_t *)&pData;
  HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t *)tmp, Size);

  /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */
  //__HAL_UART_CLEAR_OREFLAG(huart);

  /* Process Unlocked */
  __HAL_UNLOCK(huart);

  /* Enable the UART Parity Error Interrupt */
  SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);

  /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  SET_BIT(huart->Instance->CR3, USART_CR3_EIE);

  /* Enable the DMA transfer for the receiver request by setting the DMAR bit
  in the UART CR3 register */
  SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);

  return HAL_OK;
}


#define __HAL_UART_CLEAR_OREFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)


#define __HAL_UART_CLEAR_PEFLAG(__HANDLE__)     \
  do{                                           \
    __IO uint32_t tmpreg = 0x00U;               \
    tmpreg = (__HANDLE__)->Instance->SR;        \
    tmpreg = (__HANDLE__)->Instance->DR;        \
    UNUSED(tmpreg);                             \
  } while(0U)

__HAL_UART_CLEAR_OREFLAG(HANDLE)

这个宏会读uart状态寄存器和数据寄存器,当然会丢失数据。。。。。
屏蔽掉这段代码一切问题都解决了。

版权声明:本文为CSDN博主「熟能生巧啊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38177846/article/details/122573119

生成海报
点赞 0

熟能生巧啊

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐