概述
APM32F035是电机专用芯片,而电机控制算法FOC通常需要进行复杂的数**算和控制逻辑计算,因此APM32F035集成了协处理器(M0CP),以确保FOC系统的实时性和稳定性。
一.简介
协处理器(M0CP)包含硬件除法、硬件开方、三角函数生成、SVPWM 生成五段和七段式功能。
二.主要特征
- 1 个可配置 RV32ECM 内核。PM 最大为 8 KB。
- 1 个 32 位 AHB 从接口用于寄存器配置。仅支持按 32 位数据对齐据访问。
- 1 个 32 位 AHB 主接口用于访问 PM 和 DM。其中对 IBUS 主接口的响应必须为零等待。
- 1 个紧耦合的接口用于访问与 SVPWM 算法对应的 PWM 模块。
- 1 个 TX 中断(计算结束产生中断)。
- 两组参数/结果寄存器,寄存器组的切换方式可编程,由硬件或软件实现。
- 支持 Arm® Cortex®-M0+和 TXEV 接口通信。
- 可编程数据格式,Q0 至 Q15。
- 支持并优化了 CORDIC 算法,实现部分三角函数加速。支持旋转模式和矢量模式。
- 支持并优化 SVPWM 算法,支持五段式和七段式。
- 支持和优化定点平方根和除法(32/32 位)。
- 支持 atan2 运算。
三.结构框图
协处理器(M0CP) 旨在加速一些运行在Arm® Cortex®-M0+上,与FOC(Field Oriented Control,磁场定向控制)相关的应用。M0CP 与 MCU 的典型集成如下图所示。
M0CP 采用两个 32 位 AHB lite 主接口分别访问 PM 和 DM。
M0CP 可通过一个 32 位 AHB 从接口进行配置,通过 TXEV 与处理器通信,通过紧耦合握手协议接口直接更新 PWM 模块所需数据。
四.功能描述
1.CORDIC 算法描述
CORDIC(coordinate rotation digital computer,坐标旋转数字计算法)是一种硬件高效的迭代方法,通过基本的加法和移位运算,实现矢量的旋转和定向计算不再需要复杂的函数就能得出结果。
它使用旋转计算范围广泛的初等函数,同时支持旋转模式和向量模式,如下表所示。
2.SVPWM 算法描述
SVPWM(Space Vector Pulse Width Modulation,空间矢量脉宽调制)用于调制电机所需要的三相电压。5 段和 7 段的算法流程如下图所示。
注意:SVPWM 的 COUNT 模式需要将输出的占空比乘以 TMR1 的周期值,所以在不使能 TMR1 的情况下 M0CP 会一直处于 busy 状态。所以在 COUNT 模式下,应使能TMR1。
注意:操作数/结果 X,Y,Z,W(Sector)的数据存储在 X_REG/Y_REG/Z_REG/W_REG 寄存器中。
3. 除法算法描述
进行 32bit/32bit 除法时,寄存器存放数据的情况如下:
- 除数应放在 X_REG 寄存器
- 被除数应放在 Y_REG 寄存器
- (计算完成后)商(32-bit)存放在 X_REG 寄存器
- (计算完成后)余数存放在 Y_REG 寄存器
通过配置 CTRL_REG 寄存器的 ALG 位选择有符号或无符号除法。
对于有符号除法运算,取余数规则描述如下:余数的符号与被除数相同,即
余数 = 被除数 – 商×除数
例子 1:-20÷6,商为-3,余数为-2,而不是商为-4,余数为+4;
例子 2:20÷(-3),商为-7,余数为 1。
配置进来的输入的数据类型和读取出的结果的数据类型应和 ALG 的值选择对应;即计算有符号除法时,输入、输出为 int32 类型。计算无符号除法时,输入、输出为 uint32 类型。
对除法操作启动之前的除数和被除数的配置没有顺序要求。启动除法后,如果硬件运算完成, 会自动将 STAT_REG 的 BUSY 位清 0。此时,可以从相应寄存器中读取运算结果,即商和余数。
五.以下是SDK中使用M0CP外设完成SVPWM占空比输出操作的示例代码。
int main(void)
{
uint8_t model = 1;
uint8_t i;
int16_t seq_X[] = {sqrt2, -sqrt2, sqrt2, - sqrt2}; /*!< sin(45°), sin(135°), sin(-45°), sin(-135°) */
int16_t seq_Y[] = {sqrt2, sqrt2, -sqrt2, -sqrt2}; /*!< cos(45°), cos(135°), cos(-45°), cos(-135°) */
SVPWM_DATA_T svpwmInfo;
APM_MINI_Init();
APM_MINI_TMR1_PWMOutPut_Init();
M0CP_Init();
printf("This is an example of M0CP SVPWM output duty operation \r\n");
printf("Press KEY1 to start test.\r\n");
while (1)
{
if (APM_MINI_PBGetState(BUTTON_KEY1) == 0)
{
for (i = 0; i < 4; i++)
{
svpwmInfo.Ualpha = (model * seq_X);
svpwmInfo.Ubeta = (model * seq_Y);
#if M0CP_USE_FAST_IQ
/* SVPWM counter segmentation 7 and Q15 algorithm */
M0CP_Fast_IQCounterQ15SVPWM7(&svpwmInfo);
printf(">> SVPWM(%d,%d), Seg = 7, Qn = 15\r\n", svpwmInfo.Ualpha, svpwmInfo.Ubeta);
printf("<< Ca = %d%%, Cb = %d%%, Cc = %d%%, sector = %d\r\n"
, (svpwmInfo.Ca * 100 / PWM_Period)
, (svpwmInfo.Cb * 100 / PWM_Period)
, (svpwmInfo.Cc * 100 / PWM_Period)
, svpwmInfo.sector);
#else
/* SVPWM counter segmentation 7 and Q15 algorithm */
if (M0CP_IQCounterSVPWM(&svpwmInfo, M0CP_FP_Q15, M0CP_SVPWM_SEG_7) != M0CP_OK)
{
printf("M0CP SVPWM 7 Q15 segmentation timeout\r\n");
}
else
{
printf(">> SVPWM(%d,%d), Seg = 7, Qn = 15\r\n", svpwmInfo.Ualpha, svpwmInfo.Ubeta);
printf("<< Ca = %d%%, Cb = %d%%, Cc = %d%%, sector = %d\r\n"
, (svpwmInfo.Ca * 100 / PWM_Period)
, (svpwmInfo.Cb * 100 / PWM_Period)
, (svpwmInfo.Cc * 100 / PWM_Period)
, svpwmInfo.sector);
}
#endif
}
model++;
}
APM_MINI_LEDToggle(LED2);
APM_MINI_DelayMs(200);
}
}
void APM_MINI_Init(void)
{
/* Init delay function */
APM_MINI_DelayInit();
APM_MINI_LEDInit(LED2);
APM_MINI_LEDInit(LED3);
APM_MINI_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
APM_MINI_COMInit(COM1);
}
void APM_MINI_TMR1_PWMOutPut_Init(void)
{
TMR_TimeBase_T timeBaseConfig;
TMR_OCConfig_T occonfig;
GPIO_Config_T gpioconfig;
/* Enable Clock */
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);
/* Connect TMR1 to CH1 */
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_8, GPIO_AF_PIN2);
gpioconfig.mode = GPIO_MODE_AF;
gpioconfig.outtype = GPIO_OUT_TYPE_PP;
gpioconfig.pin = GPIO_PIN_8;
gpioconfig.pupd = GPIO_PUPD_NO;
gpioconfig.speed = GPIO_SPEED_50MHz;
GPIO_Config(GPIOA, &gpioconfig);
/* Set clockDivision = 1 */
timeBaseConfig.clockDivision = TMR_CKD_DIV1;
/* Up-counter */
timeBaseConfig.counterMode = TMR_COUNTER_MODE_UP;
/* Set divider = 47 .So TMR1 clock freq ~= 48/(47+1) = 1MHZ */
timeBaseConfig.div = SystemCoreClock / 1000000 - 1;
/* Set counter = 1000 */
timeBaseConfig.period = 1000;
/* Repetition counter = 0x0 */
timeBaseConfig.repetitionCounter = 0;
TMR_ConfigTimeBase(TMR1, &timeBaseConfig);
/* PWM1 mode */
occonfig.OC_Mode = TMR_OC_MODE_PWM1;
/* Idle State is reset */
occonfig.OC_Idlestate = TMR_OCIDLESTATE_RESET;
/* NIdle State is reset */
occonfig.OC_NIdlestate = TMR_OCNIDLESTATE_RESET;
/* Enable CH1N ouput */
occonfig.OC_OutputNState = TMR_OUTPUT_NSTATE_DISABLE;
/* Enable CH1 ouput */
occonfig.OC_OutputState = TMR_OUTPUT_STATE_ENABLE;
/* CH1 polarity is high */
occonfig.OC_Polarity = TMR_OC_POLARITY_HIGH;
/* CH1N polarity is high */
occonfig.OC_NPolarity = TMR_OC_NPOLARITY_HIGH;
/* Set compare value */
occonfig.Pulse = 500;
TMR_OC1Config(TMR1, &occonfig);
/* Enable PWM output */
TMR_EnablePWMOutputs(TMR1);
/* Config TMR1 M0CP */
TMR_M0CP_Init();
/* Enable TMR1 */
TMR_Enable(TMR1);
}
void M0CP_Init(void)
{
M0CP_Config_T m0cpConfigStruct;
/* Load firmware from Flash to SRAM */
M0CP_Firmware_Init();
/* Reset M0CP */
RCM_EnableAHBPeriphReset(RCM_AHB_PERIPH_M0CP);
RCM_DisableAHBPeriphReset(RCM_AHB_PERIPH_M0CP);
/* Enable M0CP clock */
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_M0CP);
/* Init M0CP hardware */
M0CP_HardInit();
M0CP_ClearStatusFlag(M0CP_FLAG_DONE);
/* Disable M0CP TX interrupt */
M0CP_DisableInterrupt(M0CP_INT_TX);
/* Wait for M0CP to be ready */
while (M0CP_ReadStatusFlag(M0CP_FLAG_BUSY) == SET)
{
}
M0CP_ConfigStructInit(&m0cpConfigStruct);
/* Configure M0CP */
m0cpConfigStruct.algebraMode = M0CP_ALG_SVPWM;
m0cpConfigStruct.bankAutoConv = M0CP_BANK_CONV_SOFTWARE;
m0cpConfigStruct.bankRegGroup = M0CP_BANK_REG_GROUP_0;
m0cpConfigStruct.fixedPoint = M0CP_FP_Q15;
m0cpConfigStruct.outputSVPWM = M0CP_SVPWM_OUT_CNT;
m0cpConfigStruct.segSVPWM = M0CP_SVPWM_SEG_7;
M0CP_Config(&m0cpConfigStruct);
}
|
|