diff --git a/README.md b/README.md index ce86fb3..1719caf 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,308 @@ -## Example Summary +# FOC 电机控制工程 -Empty project using DriverLib. -This example shows a basic empty project using DriverLib with just main file -and SysConfig initialization. +## 1. 工程概述 -## Peripherals & Pin Assignments +本工程是基于 **TI MSPM0G3507** 微控制器的 **FOC(磁场定向控制)** 电机控制方案,用于驱动无刷直流电机(BLDC),实现高精度角度控制。 -| Peripheral | Pin | Function | -| --- | --- | --- | -| SYSCTL | | | -| DEBUGSS | PA20 | Debug Clock | -| DEBUGSS | PA19 | Debug Data In Out | +### 硬件平台 +- **MCU**: MSPM0G3507(MSPM0系列,Cortex-M0+内核) +- **开发板**: LP_MSPM0G3507 LaunchPad +- **编码器**: MT6701(磁编码器,I2C接口,14位分辨率) -## BoosterPacks, Board Resources & Jumper Settings +### 软件特性 +- 完整的FOC算法实现(克拉克逆变换、帕克逆变换) +- 开环/闭环双控制模式 +- 单角度环PID控制 +- UART串口指令接收 +- 模块化代码结构 -Visit [LP_MSPM0G3507](https://www.ti.com/tool/LP-MSPM0G3507) for LaunchPad information, including user guide and hardware files. +--- -| Pin | Peripheral | Function | LaunchPad Pin | LaunchPad Settings | -| --- | --- | --- | --- | --- | -| PA20 | DEBUGSS | SWCLK | N/A | | -| PA19 | DEBUGSS | SWDIO | N/A | | +## 2. 工程结构 -### Device Migration Recommendations -This project was developed for a superset device included in the LP_MSPM0G3507 LaunchPad. Please -visit the [CCS User's Guide](https://software-dl.ti.com/msp430/esd/MSPM0-SDK/latest/docs/english/tools/ccs_ide_guide/doc_guide/doc_guide-srcs/ccs_ide_guide.html#sysconfig-project-migration) -for information about migrating to other MSPM0 devices. +``` +FOC/ +├── 3rd/ # 第三方模块 +│ ├── dfoc.c/h # FOC核心算法 +│ ├── mt6701.c/h # MT6701编码器驱动 +│ ├── pid_control.c/h # PID控制器 +│ ├── pwm.c/h # PWM输出控制 +│ ├── lowpass_filter.c/h # 低通滤波器 +│ ├── uart_redircet.c/h # printf重定向 +│ ├── delay.c/h # 延时函数 +│ └── config.h # 调试配置 +├── keil/ # Keil工程文件 +│ ├── empty_LP_MSPM0G3507_nortos_keil.uvprojx +│ ├── mspm0g3507.sct +│ └── startup_mspm0g350x_uvision.s +├── empty.c # 主程序入口 +├── empty.syscfg # SysConfig配置文件 +├── ti_msp_dl_config.c/h # SysConfig生成的配置代码 +└── README.md # 工程文档 +``` -### Low-Power Recommendations -TI recommends to terminate unused pins by setting the corresponding functions to -GPIO and configure the pins to output low or input with internal -pullup/pulldown resistor. +--- -SysConfig allows developers to easily configure unused pins by selecting **Board**→**Configure Unused Pins**. +## 3. 核心模块说明 -For more information about jumper configuration to achieve low-power using the -MSPM0 LaunchPad, please visit the [LP-MSPM0G3507 User's Guide](https://www.ti.com/lit/slau873). +### 3.1 FOC算法 (`dfoc.c`) -## Example Usage +FOC核心算法实现,包含坐标变换和控制逻辑。 -Compile, load and run the example. +**功能列表**: + +| 函数 | 功能 | +|------|------| +| `SetPhaseVoltage()` | 帕克逆变换 + 克拉克逆变换,计算三相电压 | +| `SetPwm()` | 将电压转换为占空比,输出PWM | +| `electricAngle()` | 计算电气角度 | +| `normalizeAngle()` | 角度归一化到0~2π | +| `constrain()` | 幅值限幅 | +| `FOC_Init()` | FOC初始化,校准零电角度 | +| `Set_Angle()` | 单角度环闭环控制 | +| `velocityopenloop()` | 开环速度控制 | + +**关键参数**: + +| 参数 | 值 | 说明 | +|------|-----|------| +| `voltage_limit` | 8V | 输出电压限幅 | +| `voltage_power_supply` | 12.0V | 供电电压 | + +### 3.2 编码器驱动 (`mt6701.c`) + +MT6701磁编码器驱动,通过I2C接口读取角度数据。 + +**特性**: +- 通信方式: I2C(从设备地址0x06) +- 分辨率: 14位(16384脉冲/圈) +- 支持单圈/多圈角度读取 + +**函数列表**: + +| 函数 | 功能 | +|------|------| +| `MT6701_iic_read_angel()` | I2C读取原始角度数据 | +| `GetAngle_NoTrack()` | 获取单圈角度(弧度) | +| `GetAngle()` | 获取多圈累计角度(弧度) | +| `GetVelocity()` | 计算角速度 | + +### 3.3 PID控制器 (`pid_control.c`) + +增量式PID控制器实现。 + +**参数**: +- Kp: 0.067(比例系数) +- Ki: 0.01(积分系数) +- Kd: 0(微分系数) +- 输出限幅: ±5.0 +- 积分限幅: ±5.0 + +### 3.4 PWM控制 (`pwm.c`) + +3路独立PWM输出控制。 + +**配置**: +- 定时器: TIMA0 +- 频率: 15kHz +- 通道: 3路 +- 引脚: PA21, PA22, PB20 + +### 3.5 UART重定向 (`uart_redircet.c`) + +将标准printf重定向到UART,支持串口调试输出。 + +--- + +## 4. 通信接口 + +### 4.1 UART指令接口 + +- 波特率: 115200 +- 数据位: 8位 +- 停止位: 1位 +- 校验: 无 + +**指令格式**: `T+数值+\n` + +| 示例 | 说明 | +|------|------| +| `T1.57\n` | 设置目标角度为1.57弧度 | +| `T-0.785\n` | 设置目标角度为-0.785弧度 | + +**接收机制**: +- DMA方式接收6字节数据包 +- 接收完成触发中断,解析指令并更新目标角度 + +### 4.2 I2C编码器接口 + +- 速度: 100kHz +- 用于读取MT6701编码器角度数据 + +--- + +## 5. 主程序流程 + +``` +main() + │ + ├─→ SYSCFG_DL_init() // 初始化所有硬件外设 + ├─→ DMA配置 // 配置UART接收DMA + ├─→ NVIC_EnableIRQ() // 使能UART和定时器中断 + ├─→ DL_TimerA_startCounter() // 启动PWM定时器 + ├─→ DL_TimerG_startCounter() // 启动通用定时器 + ├─→ FOC_Init(12.6) // FOC初始化,校准零电角度 + │ + └─→ while(1) + ├─→ 检查gCheckUART标志 + ├─→ 解析UART命令 → 更新Target + └─→ Set_Angle(Target) // 执行闭环控制 +``` + +**中断服务函数**: + +| 中断 | 功能 | +|------|------| +| `TIMER_0_INST_IRQHandler` | 定时器中断(调试用,LED闪烁,打印目标值) | +| `UART_0_INST_IRQHandler` | UART DMA接收完成中断,设置接收标志 | + +--- + +## 6. 关键配置参数 + +### 6.1 电机参数 (`empty.c`) + +| 参数 | 值 | 说明 | +|------|-----|------| +| `pp` | 7 | 电机极对数 | +| `Dir` | -1 | 电机方向(-1为反转,1为正转) | + +### 6.2 系统时钟 (`ti_msp_dl_config.h`) + +| 参数 | 值 | 说明 | +|------|-----|------| +| `CPUCLK_FREQ` | 32MHz | CPU系统时钟 | +| `PWM_0_INST_CLK_FREQ` | 16MHz | PWM定时器时钟 | + +### 6.3 调试配置 (`config.h`) + +```c +#define DEBUG_ENABLED true // 总调试开关 +#define DEBUG_MT_ENABLED false // 编码器调试输出 +#define DEBUG_DFOC_ENABLED false // FOC算法调试输出 +``` + +--- + +## 7. 控制模式 + +### 7.1 闭环角度控制(默认) + +通过PID控制器实现精确角度定位: + +```c +// 主循环中调用 +if(gCheckUART) +{ + gCheckUART = false; + parse_uart_cmd(); + Set_Angle(Target); // 闭环控制 +} +``` + +**控制流程**: +1. 读取编码器当前角度 +2. 计算角度误差(目标角度 - 当前角度) +3. PID控制器输出Uq电压 +4. 执行FOC变换,输出PWM + +### 7.2 开环速度控制 + +直接按目标速度旋转,无位置反馈: + +```c +// 主循环中调用(需注释掉闭环代码) +velocityopenloop(Target); +``` + +--- + +## 8. 引脚分配 + +### 8.1 PWM输出 + +| 通道 | 引脚 | 功能 | +|------|------|------| +| PWM CH0 | PA21 | U相 | +| PWM CH1 | PA22 | V相 | +| PWM CH2 | PB20 | W相 | + +### 8.2 I2C编码器 + +| 引脚 | 功能 | +|------|------| +| PB2 | I2C_SCL | +| PB3 | I2C_SDA | + +### 8.3 UART + +| 引脚 | 功能 | +|------|------| +| PA10 | UART_TX | +| PA11 | UART_RX | + +### 8.4 其他 + +| 引脚 | 功能 | +|------|------| +| PA0 | LED | +| PA19 | SWDIO | +| PA20 | SWCLK | + +--- + +## 9. 使用方法 + +### 9.1 编译 + +使用Keil MDK打开 `keil/empty_LP_MSPM0G3507_nortos_keil.uvprojx` 工程文件,编译生成固件。 + +### 9.2 烧录 + +通过XDS-110调试器将固件烧录到LP_MSPM0G3507开发板。 + +### 9.3 调试 + +1. 连接串口到PA10/PA11(UART0) +2. 打开串口助手,波特率设置为115200 +3. 发送指令格式: `T+数值+\n`,例如 `T1.57\n` +4. 观察电机响应和串口输出 + +--- + +## 10. 代码说明 + +### 10.1 编码规范 + +- 使用C语言编写,符合C99标准 +- 变量命名: 小写+下划线(如 `voltage_limit`) +- 函数命名: 首字母大写(如 `SetPhaseVoltage`) +- 中文注释,便于理解 + +### 10.2 注意事项 + +1. 修改电机极对数时需同时修改 `empty.c` 中的 `pp` 参数 +2. 编码器方向不对时调整 `Dir` 参数 +3. PID参数需根据实际电机调试优化 +4. 电压参数需与实际供电电压匹配 + +--- + +## 11. 版本信息 + +| 项目 | 说明 | +|------|------| +| SDK版本 | MSPM0 SDK 2.07.00.05 | +| 编译工具 | Keil MDK | +| 目标MCU | MSPM0G3507 | +| FOC算法 | 无传感器FOC(仅位置闭环) \ No newline at end of file diff --git a/empty.syscfg b/empty.syscfg index fa56caf..2e1736d 100644 --- a/empty.syscfg +++ b/empty.syscfg @@ -1,8 +1,8 @@ /** * These arguments were used when this file was generated. They will be automatically applied on subsequent loads * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments. - * @cliArgs --device "MSPM0G350X" --part "Default" --package "LQFP-48(PT)" --product "mspm0_sdk@2.07.00.05" - * @v2CliArgs --device "MSPM0G3507" --package "LQFP-48(PT)" --product "mspm0_sdk@2.07.00.05" + * @cliArgs --device "MSPM0G350X" --part "Default" --package "VQFN-32(RHB)" --product "mspm0_sdk@2.07.00.05" + * @v2CliArgs --device "MSPM0G3507" --package "VQFN-32(RHB)" --product "mspm0_sdk@2.07.00.05" * @versions {"tool":"1.25.0+4268"} */ @@ -11,16 +11,18 @@ */ const GPIO = scripting.addModule("/ti/driverlib/GPIO", {}, false); const GPIO1 = GPIO.addInstance(); +const GPIO2 = GPIO.addInstance(); const I2C = scripting.addModule("/ti/driverlib/I2C", {}, false); const I2C1 = I2C.addInstance(); +const MATHACL = scripting.addModule("/ti/driverlib/MATHACL"); +const MCAN = scripting.addModule("/ti/driverlib/MCAN", {}, false); +const MCAN1 = MCAN.addInstance(); const PWM = scripting.addModule("/ti/driverlib/PWM", {}, false); const PWM1 = PWM.addInstance(); const SYSCTL = scripting.addModule("/ti/driverlib/SYSCTL"); const SYSTICK = scripting.addModule("/ti/driverlib/SYSTICK"); const TIMER = scripting.addModule("/ti/driverlib/TIMER", {}, false); const TIMER1 = TIMER.addInstance(); -const UART = scripting.addModule("/ti/driverlib/UART", {}, false); -const UART1 = UART.addInstance(); /** * Write custom configuration values to the imported modules. @@ -37,6 +39,9 @@ gate7.enable = true; const multiplier2 = system.clockTree["PLL_QDIV"]; multiplier2.multiplyValue = 4; +const mux2 = system.clockTree["CANCLKMUX"]; +mux2.inputSelect = "CANCLKMUX_PLLCLK1_OUT"; + const mux4 = system.clockTree["EXHFMUX"]; mux4.inputSelect = "EXHFMUX_XTAL"; @@ -46,24 +51,36 @@ mux8.inputSelect = "HSCLKMUX_SYSPLL2X"; const mux12 = system.clockTree["SYSPLLMUX"]; mux12.inputSelect = "zSYSPLLMUX_HFCLK"; -const pinFunction4 = system.clockTree["HFXT"]; -pinFunction4.enable = true; -pinFunction4.inputFreq = 40; +const pinFunction4 = system.clockTree["HFXT"]; +pinFunction4.enable = true; +pinFunction4.inputFreq = 40; +pinFunction4.peripheral.$assign = "SYSCTL"; +pinFunction4.peripheral.hfxInPin.$assign = "PA5"; +pinFunction4.peripheral.hfxOutPin.$assign = "PA6"; -GPIO1.$name = "LED"; -GPIO1.associatedPins[0].$name = "PA0"; -GPIO1.associatedPins[0].pin.$assign = "PA0"; +GPIO1.$name = "GPIO_GRP_0"; +GPIO1.port = "PORTA"; +GPIO1.associatedPins[0].$name = "PIN_0"; +GPIO1.associatedPins[0].direction = "INPUT"; +GPIO1.associatedPins[0].pin.$assign = "PA14"; const Board = scripting.addModule("/ti/driverlib/Board", {}, false); Board.peripheral.$assign = "DEBUGSS"; Board.peripheral.swclkPin.$assign = "PA20"; Board.peripheral.swdioPin.$assign = "PA19"; +GPIO2.$name = "GPIO_GRP_1"; +GPIO2.port = "PORTA"; +GPIO2.associatedPins[0].$name = "PIN_1"; +GPIO2.associatedPins[0].direction = "INPUT"; +GPIO2.associatedPins[0].pin.$assign = "PA15"; + I2C1.$name = "I2C_1"; I2C1.advAnalogGlitchFilter = "DISABLED"; I2C1.basicEnableController = true; -I2C1.peripheral.sdaPin.$assign = "PB3"; -I2C1.peripheral.sclPin.$assign = "PB2"; +I2C1.peripheral.$assign = "I2C1"; +I2C1.peripheral.sdaPin.$assign = "PA16"; +I2C1.peripheral.sclPin.$assign = "PA17"; I2C1.sdaPinConfig.hideOutputInversion = scripting.forceWrite(false); I2C1.sdaPinConfig.onlyInternalResistor = scripting.forceWrite(false); I2C1.sdaPinConfig.passedPeripheralType = scripting.forceWrite("Digital"); @@ -73,6 +90,13 @@ I2C1.sclPinConfig.onlyInternalResistor = scripting.forceWrite(false); I2C1.sclPinConfig.passedPeripheralType = scripting.forceWrite("Digital"); I2C1.sclPinConfig.$name = "ti_driverlib_gpio_GPIOPinGeneric3"; +MCAN1.$name = "MCAN0"; +MCAN1.peripheral.$assign = "CANFD0"; +MCAN1.peripheral.rxPin.$assign = "PA27"; +MCAN1.peripheral.txPin.$assign = "PA26"; +MCAN1.txPinConfig.$name = "ti_driverlib_gpio_GPIOPinGeneric7"; +MCAN1.rxPinConfig.$name = "ti_driverlib_gpio_GPIOPinGeneric8"; + PWM1.$name = "PWM_0"; PWM1.ccIndex = [0,1,2]; PWM1.clockDivider = 2; @@ -83,14 +107,14 @@ PWM1.PWM_CHANNEL_1.$name = "ti_driverlib_pwm_PWMTimerCC1"; PWM1.ccp0PinConfig.$name = "ti_driverlib_gpio_GPIOPinGeneric4"; PWM1.ccp1PinConfig.$name = "ti_driverlib_gpio_GPIOPinGeneric5"; PWM1.peripheral.$assign = "TIMA0"; -PWM1.peripheral.ccp0Pin.$assign = "PA21"; -PWM1.peripheral.ccp1Pin.$assign = "PA22"; -PWM1.peripheral.ccp2Pin.$assign = "PB20"; +PWM1.peripheral.ccp0Pin.$assign = "PA8"; +PWM1.peripheral.ccp1Pin.$assign = "PA9"; +PWM1.peripheral.ccp2Pin.$assign = "PA10"; PWM1.PWM_CHANNEL_2.$name = "ti_driverlib_pwm_PWMTimerCC2"; PWM1.ccp2PinConfig.$name = "ti_driverlib_gpio_GPIOPinGeneric6"; SYSCTL.forceDefaultClkConfig = true; -SYSCTL.peripheral.$assign = "SYSCTL"; +SYSCTL.clockTreeEn = true; SYSTICK.periodEnable = true; SYSTICK.systickEnable = true; @@ -105,41 +129,5 @@ TIMER1.timerClkSrc = "LFCLK"; TIMER1.timerPeriod = "1"; TIMER1.peripheral.$assign = "TIMG0"; -UART1.$name = "UART_0"; -UART1.rxFifoThreshold = "DL_UART_RX_FIFO_LEVEL_ONE_ENTRY"; -UART1.targetBaudRate = 115200; -UART1.uartClkDiv = "8"; -UART1.enableFIFO = true; -UART1.txFifoThreshold = "DL_UART_TX_FIFO_LEVEL_ONE_ENTRY"; -UART1.enabledDMATXTriggers = "DL_UART_DMA_INTERRUPT_TX"; -UART1.enabledInterrupts = ["DMA_DONE_RX"]; -UART1.enabledDMARXTriggers = "DL_UART_DMA_INTERRUPT_RX"; -UART1.peripheral.$assign = "UART0"; -UART1.peripheral.rxPin.$assign = "PA11"; -UART1.peripheral.txPin.$assign = "PA10"; -UART1.txPinConfig.$name = "ti_driverlib_gpio_GPIOPinGeneric0"; -UART1.rxPinConfig.$name = "ti_driverlib_gpio_GPIOPinGeneric1"; -UART1.DMA_CHANNEL_TX.$name = "DMA_CH0"; -UART1.DMA_CHANNEL_TX.addressMode = "b2f"; -UART1.DMA_CHANNEL_TX.srcLength = "BYTE"; -UART1.DMA_CHANNEL_TX.dstLength = "BYTE"; -UART1.DMA_CHANNEL_TX.peripheral.$assign = "DMA_CH0"; -UART1.DMA_CHANNEL_RX.$name = "DMA_CH1"; -UART1.DMA_CHANNEL_RX.addressMode = "f2b"; -UART1.DMA_CHANNEL_RX.srcLength = "BYTE"; -UART1.DMA_CHANNEL_RX.dstLength = "BYTE"; -UART1.DMA_CHANNEL_RX.transferMode = "FULL_CH_REPEAT_SINGLE"; - const ProjectConfig = scripting.addModule("/ti/project_config/ProjectConfig", {}, false); ProjectConfig.migrationCondition = true; - -/** - * Pinmux solution for unlocked pins/peripherals. This ensures that minor changes to the automatic solver in a future - * version of the tool will not impact the pinmux you originally saw. These lines can be completely deleted in order to - * re-solve from scratch. - */ -pinFunction4.peripheral.$suggestSolution = "SYSCTL"; -pinFunction4.peripheral.hfxInPin.$suggestSolution = "PA5"; -pinFunction4.peripheral.hfxOutPin.$suggestSolution = "PA6"; -I2C1.peripheral.$suggestSolution = "I2C1"; -UART1.DMA_CHANNEL_RX.peripheral.$suggestSolution = "DMA_CH1";