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

1. 源由

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

2. 框架设计

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

在这里插入图片描述

2.1 初始化 telem_UART_Init

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

  1. 初始化 USART 和 GPIO 结构体:准备配置结构体。
  2. 启用外设时钟:启用 USART1 和 GPIOB 的时钟。
  3. 配置 GPIO
    • 配置 GPIOB 的引脚 6 为 USART1 的替代功能,设置为开漏输出、上拉模式和低速。
  4. 配置 NVIC 中断
    • 设置 USART1 的中断优先级并启用中断。
  5. 配置 DMA
    • 设置 DMA 通道 3 的外设请求、数据传输方向、优先级、模式、地址递增、数据对齐等参数。
  6. 配置 USART
    • 设置 USART1 的预分频器、波特率、数据宽度、停止位、奇偶校验、传输方向、过采样等参数。
    • 配置 FIFO 阈值和禁用 FIFO,配置半双工模式。
  7. 启用 USART
    • 启用 USART1,并等待发送和接收确认标志。
  8. 配置 DMA 传输
    • 设置 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

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

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

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

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

    • 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. 设置温度

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

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

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

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

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

    • 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开源代码之工程结构

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值