AM32开源Telemetry代码深度解析

AM32开源代码之代码分析 – Telemetry

  • 1. 源由
  • 2. 框架设计
  • 2.1 初始化 `telem_UART_Init`
  • 2.2 DMA发送数据
  • 2.3 CRC处理函数
  • 2.4 创建电传报文
  • 3. 总结
  • 4. 参考资料
  • 1. 源由

    ESC电传就是电子调速器用数字形式反馈当前工作状态的报文格式。

    2. 框架设计

    STM32G071G MCU设计图,使用PB6作为ESC的电传引脚。

    2.1 初始化 telem_UART_Init

    USART1 的初始化,并配置了相关的 GPIO 和 DMA:

    1. 初始化 USART 和 GPIO 结构体:准备配置结构体。
    2. 启用外设时钟:启用 USART1 和 GPIOB 的时钟。
    3. 配置 GPIO
    4. 配置 GPIOB 的引脚 6 为 USART1 的替代功能,设置为开漏输出、上拉模式和低速。
    5. 配置 NVIC 中断
    6. 设置 USART1 的中断优先级并启用中断。
    7. 配置 DMA
    8. 设置 DMA 通道 3 的外设请求、数据传输方向、优先级、模式、地址递增、数据对齐等参数。
    9. 配置 USART
    10. 设置 USART1 的预分频器、波特率、数据宽度、停止位、奇偶校验、传输方向、过采样等参数。
    11. 配置 FIFO 阈值和禁用 FIFO,配置半双工模式。
    12. 启用 USART
    13. 启用 USART1,并等待发送和接收确认标志。
    14. 配置 DMA 传输
    15. 设置 DMA 传输的源地址、目标地址和数据长度,启用 DMA 的传输完成和传输错误中断。
    telem_UART_Init()
    │
    ├── 初始化 USART 和 GPIO 结构体
    │   ├── LL_USART_InitTypeDef USART_InitStruct = { 0 }
    │   └── LL_GPIO_InitTypeDef GPIO_InitStruct = { 0 }
    │
    ├── 启用外设时钟
    │   ├── LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1)   // 启用 USART1 外设时钟
    │   └── LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB)     // 启用 GPIOB 外设时钟
    │
    ├── 配置 GPIO
    │   └── GPIO_InitStruct 配置
    │       ├── Pin = LL_GPIO_PIN_6                            // 配置为引脚 6
    │       ├── Mode = LL_GPIO_MODE_ALTERNATE                   // 配置为替代功能模式
    │       ├── Speed = LL_GPIO_SPEED_FREQ_LOW                  // 设置速度为低
    │       ├── OutputType = LL_GPIO_OUTPUT_OPENDRAIN            // 设置为开漏输出
    │       ├── Pull = LL_GPIO_PULL_UP                           // 上拉
    │       └── Alternate = LL_GPIO_AF_0                         // 设置为替代功能 AF0
    │   └── LL_GPIO_Init(GPIOB, &GPIO_InitStruct)                 // 初始化 GPIOB 引脚 6
    │
    ├── 配置 NVIC 中断
    │   ├── NVIC_SetPriority(USART1_IRQn, 3)                      // 设置 USART1 中断优先级为 3
    │   └── NVIC_EnableIRQ(USART1_IRQn)                           // 启用 USART1 中断
    │
    ├── 配置 DMA
    │   ├── LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMAMUX_REQ_USART1_TX) // 设置 DMA 传输请求为 USART1_TX
    │   ├── LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_MEMORY_TO_PERIPH) // 设置 DMA 传输方向为内存到外设
    │   ├── LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_LOW) // 设置 DMA 优先级为低
    │   ├── LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL) // 设置 DMA 模式为普通模式
    │   ├── LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT) // 设置外设地址不递增
    │   ├── LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT) // 设置内存地址递增
    │   ├── LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE) // 设置外设数据大小为字节
    │   └── LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE) // 设置内存数据大小为字节
    │
    ├── 配置 USART
    │   ├── USART_InitStruct 配置
    │   │   ├── PrescalerValue = LL_USART_PRESCALER_DIV1           // 设置预分频器为 1
    │   │   ├── BaudRate = 115200                                 // 设置波特率为 115200
    │   │   ├── DataWidth = LL_USART_DATAWIDTH_8B                  // 设置数据宽度为 8 位
    │   │   ├── StopBits = LL_USART_STOPBITS_1                     // 设置停止位为 1 位
    │   │   ├── Parity = LL_USART_PARITY_NONE                      // 设置无奇偶校验
    │   │   ├── TransferDirection = LL_USART_DIRECTION_TX_RX       // 设置传输方向为 TX/RX
    │   │   └── OverSampling = LL_USART_OVERSAMPLING_16            // 设置过采样为 16
    │   ├── LL_USART_Init(USART1, &USART_InitStruct)               // 初始化 USART1
    │   ├── LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8) // 设置 TX FIFO 阈值为 1/8
    │   ├── LL_USART_SetRXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8) // 设置 RX FIFO 阈值为 1/8
    │   └── LL_USART_DisableFIFO(USART1)                           // 禁用 FIFO
    │   └── LL_USART_ConfigHalfDuplexMode(USART1)                  // 配置半双工模式
    │
    ├── 启用 USART
    │   ├── LL_USART_Enable(USART1)                               // 启用 USART1
    │   └── 等待 USART 启用完成
    │       └── while ((!(LL_USART_IsActiveFlag_TEACK(USART1))) || (!(LL_USART_IsActiveFlag_REACK(USART1)))) {} // 等待发送和接收确认标志
    │
    └── 配置 DMA
        ├── LL_DMA_ConfigAddresses(
        │   ├── DMA1, LL_DMA_CHANNEL_3, (uint32_t)aTxBuffer,       // 设置 DMA 传输地址
        │   └── LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_TRANSMIT),
        │   └── LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3)
        ├── LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, nbDataToTransmit) // 设置 DMA 传输数据长度
        ├── LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_3)            // 启用 DMA 传输完成中断
        └── LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_3)            // 启用 DMA 传输错误中断
    

    2.2 DMA发送数据

    配置 USART1 的 DMA 发送操作,并设置了数据长度。主要步骤包括:

    1. 设置 USART1 传输方向为 TX

    2. LL_USART_SetTransferDirection(USART1, LL_USART_DIRECTION_TX):配置 USART1 的数据传输方向为发送(TX)。
    3. 设置 DMA 数据长度

    4. LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, nbDataToTransmit):设置 DMA 通道 3 的数据传输长度为 nbDataToTransmit
    5. 启用 USART1 的 DMA 发送请求

    6. LL_USART_EnableDMAReq_TX(USART1):启用 USART1 的 DMA 发送请求,允许通过 DMA 发送数据。
    7. 启用 DMA 通道 3

    8. LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3):启用 DMA 通道 3,开始数据传输。
    9. 恢复 USART1 传输方向为 RX

    10. LL_USART_SetTransferDirection(USART1, LL_USART_DIRECTION_RX):将 USART1 的传输方向恢复为接收(RX),准备接收数据。
    send_telem_DMA()
    │
    ├── 设置 USART1 传输方向为 TX
    │   └── LL_USART_SetTransferDirection(USART1, LL_USART_DIRECTION_TX)
    │
    ├── 设置 DMA 数据长度
    │   └── LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, nbDataToTransmit)
    │
    ├── 启用 USART1 的 DMA 发送请求
    │   └── LL_USART_EnableDMAReq_TX(USART1)
    │
    ├── 启用 DMA 通道以开始数据传输
    │   └── LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3)
    │
    └── 恢复 USART1 传输方向为 RX
        └── LL_USART_SetTransferDirection(USART1, LL_USART_DIRECTION_RX)
    

    2.3 CRC处理函数

    uint8_t update_crc8(uint8_t crc, uint8_t crc_seed)
    {
        uint8_t crc_u, i;
        crc_u = crc;
        crc_u ^= crc_seed;
        for (i = 0; i < 8; i++)
            crc_u = (crc_u & 0x80) ? 0x7 ^ (crc_u << 1) : (crc_u << 1);
        return (crc_u);
    }
    
    uint8_t get_crc8(uint8_t* Buf, uint8_t BufLen)
    {
        uint8_t crc = 0, i;
        for (i = 0; i < BufLen; i++)
            crc = update_crc8(Buf[i], crc);
        return (crc);
    }
    

    2.4 创建电传报文

    创建了一个用于数据传输的电报包,并将数据存储在 aTxBuffer 数组中。主要步骤包括:

    1. 设置温度

    2. aTxBuffer[0] = temp:将温度值存储到 aTxBuffer 的第一个字节。
    3. 设置电压

    4. aTxBuffer[1] = (voltage >> 8) & 0xFF:将电压的高字节存储到 aTxBuffer 的第二个字节。
    5. aTxBuffer[2] = voltage & 0xFF:将电压的低字节存储到 aTxBuffer 的第三个字节。
    6. 设置电流

    7. aTxBuffer[3] = (current >> 8) & 0xFF:将电流的高字节存储到 aTxBuffer 的第四个字节。
    8. aTxBuffer[4] = current & 0xFF:将电流的低字节存储到 aTxBuffer 的第五个字节。
    9. 设置消耗

    10. aTxBuffer[5] = (consumption >> 8) & 0xFF:将消耗的高字节存储到 aTxBuffer 的第六个字节。
    11. aTxBuffer[6] = consumption & 0xFF:将消耗的低字节存储到 aTxBuffer 的第七个字节。
    12. 设置电机转速

    13. aTxBuffer[7] = (e_rpm >> 8) & 0xFF:将电机转速的高字节存储到 aTxBuffer 的第八个字节。
    14. aTxBuffer[8] = e_rpm & 0xFF:将电机转速的低字节存储到 aTxBuffer 的第九个字节。
    15. 计算并设置 CRC 校验码

    16. aTxBuffer[9] = get_crc8(aTxBuffer, 9):计算 aTxBuffer 前 9 个字节的 CRC8 校验码,并将结果存储在第十个字节(aTxBuffer[9])。
    makeTelemPackage(temp, voltage, current, consumption, e_rpm)
    │
    ├── 设置温度
    │   └── aTxBuffer[0] = temp
    │
    ├── 设置电压
    │   ├── aTxBuffer[1] = (voltage >> 8) & 0xFF     // 电压高字节
    │   └── aTxBuffer[2] = voltage & 0xFF             // 电压低字节
    │
    ├── 设置电流
    │   ├── aTxBuffer[3] = (current >> 8) & 0xFF     // 电流高字节
    │   └── aTxBuffer[4] = current & 0xFF             // 电流低字节
    │
    ├── 设置消耗
    │   ├── aTxBuffer[5] = (consumption >> 8) & 0xFF // 消耗高字节
    │   └── aTxBuffer[6] = consumption & 0xFF         // 消耗低字节
    │
    ├── 设置电机转速
    │   ├── aTxBuffer[7] = (e_rpm >> 8) & 0xFF       // 电机转速高字节
    │   └── aTxBuffer[8] = e_rpm & 0xFF               // 电机转速低字节
    │
    └── 计算并设置 CRC 校验码
        └── aTxBuffer[9] = get_crc8(aTxBuffer, 9)     // 计算 CRC8 校验码并存入第 9 字节
    

    3. 总结

    这个好简单,就是吧通过报文将以下电子调速器的当前工作状态传送回去就完了。

  • 温度
  • 电压
  • 电流
  • 电量
  • 转速
  • 4. 参考资料

    【1】BLDC ESC 无刷直流电子调速器简介
    【2】BLDC ESC 无刷直流电子调速器工作原理
    【3】BLDC ESC 无刷直流电子调速器工作过程
    【4】BLDC ESC 无刷直流电子调速器驱动方式
    【5】AM32开源代码之工程结构

    作者:lida2003

    物联沃分享整理
    物联沃-IOTWORD物联网 » AM32开源Telemetry代码深度解析

    发表回复