现在开始分析C语言的代码(建议将代码复制到notepad 为方便读者实验,每个代码都是独立的子模块,复制到工程中即可编译操作:
一、配置高级定时器TIM1产生6路互补PWM,带刹车保护
详细配置代码如下,复制以下程序段main.c可直接输出到中间PWM波形(保证BKIN下拉),方便读者验证:
static void TIM1_GPIO_Config( void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9| GPIO_Pin_10 | GPIO_Pin_11; //CH1--A8 CH2--A9 CH3--A10 CH4-A11 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 CH1N-B13 CH2N-B14 CH3N-B15 BKIN-B12 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14 | GPIO_Pin_15; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //BKIN-B12 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_PinLockConfig(GPIOA, GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 ); //锁住高侧IO口的配置寄存器,避免后面误修改 } #define CKTIM ((u32)72000000uL) //主频 #define PWM_PRSC ((u8)0) //TIM1分频系数 #define PWM_FREQ ((u16) 15000) //PWM频率(Hz) #define PWM_PERIOD ((u16) (CKTIM / (u32)(2 * PWM_FREQ *(PWM_PRSC+1)))) #define REP_RATE (1) //该参数可以调整电流环的刷新频率,刷新周期:(REP_RATE + 1)/(2*PWM_FREQ) 秒 //因为电流环的采样是靠TIM1来触发的 #define DEADTIME_NS ((u16)1000) //死区时间(ns),范围:0-3500 #define DEADTIME (u16)((unsigned long long)CKTIM/2 * (unsigned long long)DEADTIME_NS/1000000000uL) static void TIM1_Mode_Config( void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM1_BDTRInitStructure; NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD; //计数周期 TIM_TimeBaseStructure.TIM_Prescaler = PWM_PRSC; //分频系数 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV2; //设置外部时钟TIM1ETR的滤波时间 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1; //中央对齐模式1,从0计数到 TIM_Period 然后开始减到0,循环 TIM_TimeBaseStructure.TIM_RepetitionCounter = REP_RATE; //重复计数,就是重复溢出多少次才产生一个溢出中断(产生更新事件,用来触发ADC采样) TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能CHx的PWM输出 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //互补输出使能,使能CHxN的PWM输出 TIM_OCInitStructure.TIM_Pulse = 800; //设置跳变值,当计数器计数到这个值时,电平发生跳变 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //CHx有效电平的极性为高电平(高侧) TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; //CHxN有效电平的极性为高电平(低侧) TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; //在空闲时CHx输出低(高侧), 调用TIM_CtrlPWMOutputs(TIM1, DISABLE)后就是空闲状态。 TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set; //在空闲时CHxN输出高(低侧),打开低侧管子可以用来锁电机 //TIM_OCIdleState 和 TIM_OCNIdleState不能同时为高 TIM_OC1Init(TIM1, &TIM_OCInitStructure); //配置CH1 TIM_OCInitStructure.TIM_Pulse = 800; TIM_OC2Init(TIM1, &TIM_OCInitStructure); //配置CH2 TIM_OCInitStructure.TIM_Pulse = 800; TIM_OC3Init(TIM1, &TIM_OCInitStructure); //配置CH3 //设置刹车特性,死区时间,锁电平,OSSI,OSSR状态和AOE(自动输出使能) TIM1_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; //MOE=1且定时器不工作时,CHx和CHxN的输出状态 TIM1_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; //MOE=0且定时器不工作时,CHx和CHxN的输出状态(详情看用户手册,一般都是ENABLE,不用深究) TIM1_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1; //BDTR寄存器写保护等级,防止软件错误误写。 TIM1_BDTRInitStructure.TIM_DeadTime = DEADTIME; //设置死区时间 TIM1_BDTRInitStructure.TIM_Break = TIM_Break_Enable; //使能TIM1刹车输入(BKIN),要把BKIN引脚拉低才有PWM输出 TIM1_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; //刹车输入(BKIN)输入高电平有效 TIM1_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; //刹车有效标志只能被软件清除,不能被自动清除 TIM_BDTRConfig(TIM1, &TIM1_BDTRInitStructure); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //4个抢先级、4个子优先级 /* Enable the TIM1 BRK Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update); //使用TIM1的更新事件作为触发输出,这个输出可以触发ADC进行采样,电流环的采样 TIM_ClearITPendingBit(TIM1, TIM_IT_Break); //清除刹车中断,BKIN输入导致的中断 TIM_ITConfig(TIM1, TIM_IT_Break, ENABLE); //使能刹车中断 TIM_CtrlPWMOutputs(TIM1, ENABLE); //PWM输出使能 TIM_Cmd(TIM1, ENABLE); //使能TIM1 } void TIM1_PWM_Init( void) { TIM1_GPIO_Config(); TIM1_Mode_Config(); } /******************************************************************************* * Function Name : TIM1_BRK_IRQHandler * Description : This function handles TIM1 Break interrupt request. *******************************************************************************/ void TIM1_BRK_IRQHandler( void) { //关闭IGBT,并报错 TIM_ClearITPendingBit(TIM1, TIM_IT_Break); }
1、配置TIM1的CH1--A8、CH2--A9、CH3--A10、CH4-A11、CH1N-B13、CH2N-B14、CH3N-B15、BKIN-B12
BKIN作为报警信号或者刹车信号的输入,当检测此信号时,TIM1的PWM会硬件上停止输出,实时性好,起到保护硬件电路的作用。
2、观察SVPWM的PWM波形是对称的:
正好配置TIM1为中央对齐模式1,在上面代码的配置中,载波周期为15KHz,TIM_Period(ARR)=2400,CH1的TIM_Pulse(CCR)=800。采用的PWM1模式,即CNT小于CCR时,输出有效电平,大于CCR小于ARR时,输出无效电平,又配置CHx的有效电平为高电平,CHxN的有效电平为高电平,则可以得到下面的PWM波形:
如果CHxN的有效电平是低电平,则输出的CHx和CHxN的波形是相同的。(可能CHx和CHxN有效电平的叫法相反)
3、配置CHx和CHxN空闲时的电平,调用TIM_CtrlPWMOutputs(TIM1, DISABLE)后,就进入空闲状态了,高侧没什么用,让空闲时低侧的管子导通,可以使相线连在一起,起到锁定的作用。
4、改变 REP_RATE 的值,可以更改TRGO信号输出的频率。因为相电流采样由TIM1的TRGO信号触发,故更改REP_RATE可以调整电流环的计算频率(每次相电流采样后,会进行一次FOC运算)。采样频率关系:(2*PWM_FREQ)/(REP_RATE + 1),如:当PWM_FREQ=15KHz,REP_RATE=0,则采样频率为30KHz。
5、TIM_OSSRState和TIM_OSSIState直接Enable就可以了,详情可以去看用户手册。
6、使用TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);函数设置TRGO信号的产生源,TIM_TRGOSource_Update参数代表TIM1产生一次更新事件,就输出一次TRGO信号。TRGO信号用来触发相电流的采样。
当然也可以使用TIM1的CH4来触发相电流采样,参数为TIM_TRGOSource_OC4Ref ,再打开CH4,并配置CH4的比较值,比如配置比较值为PWM_PERIOD-5。这样当CNT计数到PWM_PERIOD-5时就会触发相电流的ADC采样,这种方法比较灵活,可以合理设置CH4的比较值,来让相电流采样点避开开关噪声。
二、配置双ADC模式和规则组、注入组,其中注入组由TIM1的TRGO触发
//A相电流采样 #define PHASE_A_ADC_CHANNEL ADC_Channel_11 #define PHASE_A_GPIO_PORT GPIOC #define PHASE_A_GPIO_PIN GPIO_Pin_1 //B相电流采样 #define PHASE_B_ADC_CHANNEL ADC_Channel_10 #define PHASE_B_GPIO_PORT GPIOC #define PHASE_B_GPIO_PIN GPIO_Pin_0 //读取散热器温度(过热保护) #define TEMP_FDBK_CHANNEL ADC_Channel_13 #define TEMP_FDBK_CHANNEL_GPIO_PORT GPIOC #define TEMP_FDBK_CHANNEL_GPIO_PIN GPIO_Pin_3 //读取总线电压值(过压、欠压保护) #define BUS_VOLT_FDBK_CHANNEL ADC_Channel_14 #define BUS_VOLT_FDBK_CHANNEL_GPIO_PORT GPIOC #define BUS_VOLT_FDBK_CHANNEL_GPIO_PIN GPIO_Pin_4 //读取电位器值,可以用来调速等 #define POT1_VOLT_FDBK_CHANNEL ADC_Channel_12 #define POT1_VOLT_FDBK_CHANNEL_GPIO_PORT GPIOC #define POT1_VOLT_FDBK_CHANNEL_GPIO_PIN GPIO_Pin_2 //读取母线电流值(过流保护) #define BUS_SHUNT_CURR_CHANNEL ADC_Channel_15 #define BUS_SHUNT_CURR_CHANNEL_GPIO_PORT GPIOC #define BUS_SHUNT_CURR_CHANNEL_GPIO_PIN GPIO_Pin_5 //读取刹车电阻电流(刹车电阻过流保护) #define BRK_SHUNT_CURR_CHANNEL ADC_Channel_7 #define BRK_SHUNT_CURR_CHANNEL_GPIO_PORT GPIOA #define BRK_SHUNT_CURR_CHANNEL_GPIO_PIN GPIO_Pin_7 //备用通道 #define AIN0_VOLT_FDBK_CHANNEL ADC_Channel_8 #define AIN0_VOLT_FDBK_CHANNEL_GPIO_PORT GPIOB #define AIN0_VOLT_FDBK_CHANNEL_GPIO_PIN GPIO_Pin_0 //备用导通 #define AIN1_VOLT_FDBK_CHANNEL ADC_Channel_9 #define AIN1_VOLT_FDBK_CHANNEL_GPIO_PORT GPIOB #define AIN1_VOLT_FDBK_CHANNEL_GPIO_PIN GPIO_Pin_1 #define ADC1_DR_Address ((uint32_t)0x4001244C) //ADC数据寄存器地址 #define BufferLenght 36 volatile u32 ADC_DualConvertedValueTab[BufferLenght]; volatile u16 ADC1_RegularConvertedValueTab[BufferLenght]; volatile u16 ADC2_RegularConvertedValueTab[BufferLenght]; static u16 hPhaseAOffset; static u16 hPhaseBOffset; void ADC_DMA_Init(void) { u8 bIndex; GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ADCCLK = PCLK2/6=72M/6=12MHz,ADC最大频率不能超过14MHz RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //DMA1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOE, ENABLE); GPIO_StructInit(&GPIO_InitStructure); //Fills each GPIO_InitStruct member with its default value GPIO_InitStructure.GPIO_Pin = PHASE_A_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(PHASE_A_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = PHASE_B_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(PHASE_B_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = TEMP_FDBK_CHANNEL_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(TEMP_FDBK_CHANNEL_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = BUS_VOLT_FDBK_CHANNEL_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(BUS_VOLT_FDBK_CHANNEL_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = POT1_VOLT_FDBK_CHANNEL_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(POT1_VOLT_FDBK_CHANNEL_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = BUS_SHUNT_CURR_CHANNEL_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(BUS_SHUNT_CURR_CHANNEL_GPIO_PORT, &GPIO_InitStructure); GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = BRK_SHUNT_CURR_CHANNEL_GPIO_PIN;