下面詳細(xì)說(shuō)明程序的結(jié)構(gòu)和功能的實(shí)現(xiàn)。
對(duì)于一個(gè)比較復(fù)雜的項(xiàng)目來(lái)說(shuō),程序框架的好壞決定了后續(xù)的可維護(hù)性,功能可擴(kuò)展性。在這個(gè)項(xiàng)目中,筆者參考了MX的數(shù)字電源插件X-CUBE-DPower中的例程框架,將該框架移植到了本項(xiàng)目中。在本文的附件中,有st官方開(kāi)發(fā)板B-G474-DPOW1的例子程序,本文中的代碼大部分來(lái)自于這些例程。
在這個(gè)程序框架中,框架本質(zhì)上是一個(gè)狀態(tài)機(jī),這個(gè)狀態(tài)機(jī)做為main()函數(shù)中的主循環(huán)的主體,實(shí)現(xiàn)了電源工作狀態(tài)的切換、LED狀態(tài)的指示、告警、開(kāi)機(jī)軟啟動(dòng)和恒壓恒流環(huán)路的計(jì)算等功能。
從上圖可以看到狀態(tài)機(jī)的一個(gè)工作流程,需要在本程序建立這個(gè)狀態(tài)機(jī)框架。
因?yàn)閺南旅骈_(kāi)始的大部分的代碼編寫(xiě)都不在MX生成的代碼中進(jìn)行,為了以后不干擾用戶自己編寫(xiě)的代碼,需要在程序的工程目錄中再創(chuàng)建一個(gè)新的目錄,專(zhuān)門(mén)保存和管理用戶自己編寫(xiě)的代碼。
筆者創(chuàng)建了個(gè)“app”的目錄,里面有四個(gè)文件,如下圖:
LED.*為L(zhǎng)ED燈的相關(guān)代碼,Status_Machine.*為狀態(tài)機(jī)的相關(guān)代碼。
在進(jìn)行狀態(tài)機(jī)框架建立之前,要先說(shuō)一下LED燈的工作方式。
下圖是三個(gè)LED輸出引腳定義。
在本項(xiàng)目中,用到了三個(gè)LED燈,LED引腳分別是PB9——LED1、PE0——LED2、PE1——LED3。每個(gè)LED燈都有四種工作方式:1常亮、2常滅、3連續(xù)閃(閃動(dòng)的時(shí)間可設(shè)置)、4連續(xù)閃n次間隔一段時(shí)間(m毫秒)后,再閃n次,其中閃動(dòng)次數(shù)n,連續(xù)閃動(dòng)間隔時(shí)間和下次閃動(dòng)的間隔時(shí)間m都可以設(shè)定。
當(dāng)定義好LED用到的數(shù)據(jù)類(lèi)型后,將LED的功能函數(shù)移植過(guò)來(lái),在stm32g4xx_it.c文件中的系統(tǒng)中斷——tick定時(shí)器中斷服務(wù)中將LED計(jì)數(shù)器遞減。當(dāng)需要設(shè)置LED工作狀態(tài)機(jī)用下面函數(shù):
LED_SetParams(&LED1_Status, LED_BLINK_N, LED_RUN_BLINK_NUM, LED_BLINK_PERIOD_MS, LED_BLINK_REPETITION_PERIOD_MS);
LED_SetParams(&LED2_Status, LED_BLINK_INF, 0, LED_BLINK_PERIOD_LONG_MS,0);
LED_SetParams(&LED3_Status, LED_BLINK_N, LED_STARTUP_BLINK_NUM, LED_BLINK_PERIOD_SHORT_MS, LED_BLINK_REPETITION_PERIOD_MS);
然后在main.c的主循環(huán)中運(yùn)行
while (1)
{
LED_Task(&LED1_Status);
LED_Task(&LED2_Status);
LED_Task(&LED3_Status);
}
這樣,每個(gè)LED有四個(gè)狀態(tài),通過(guò)三個(gè)LED燈不同狀態(tài)的組合,就能指示多種芯片內(nèi)部運(yùn)行狀態(tài),在調(diào)試過(guò)程中非常有用。
在進(jìn)入狀態(tài)機(jī)循環(huán)之前,應(yīng)該先將需要的外設(shè)使能。
/* Init 2p2z using the FMAC */
if (__3p3zInitFmacInt16( &USER_APPL_FMAC, &cntrlFmac,
A1, A2, 0.0,
B0, B1, B2, 0.0,
post_shift, DUTY_TICKS_MIN, DUTY_TICKS_MAX ) != HAL_OK)
{
/* Configuration Error */
Init_Error_Handler();
}
/* Init 2p2z using the CPU */
CNTRL_3p3zInit(&myDcdc.iLoop, iREF,
iA1, iA2, 0.0,
iB0, iB1, iB2, 0.0,
iK, (float_t)iDUTY_TICKS_MIN, (float_t)iDUTY_TICKS_MAX );
/* Start the FMAC and wait for DMA transfer */
if (__StartFmacInt16( &USER_APPL_FMAC, &cntrlFmac ) != HAL_OK)
{
/* Configuration Error */
Init_Error_Handler();
}
/* Initialise soft start ramp */
CNTRL_RampFloatConfig( &myDcdc.vRamp, 0, (VOUT_SET*VOUT_ADC_COFF), STARTUP_DCDC_RAMP_DURATION, 1000 );
/* Perform an ADCx automatic self-calibration and enable ADC */
if(HAL_ADCEx_Calibration_Start(&USER_APPL_ADC3, ADC_SINGLE_ENDED) != HAL_OK)
{
/* ADC initialization Error */
Init_Error_Handler();
}
/* Start ADCx with DMA request */
if(HAL_ADC_Start_DMA(&USER_APPL_ADC3, (uint32_t *) &USER_APPL_FMAC.Instance->WDATA, 1) != HAL_OK)
{
/* ADC initiliazation Error */
Init_Error_Handler();
}
if(HAL_ADCEx_Calibration_Start(&USER_APPL_ADC1, ADC_SINGLE_ENDED) != HAL_OK)
{
/* ADC initialization Error */
Init_Error_Handler();
}
/* Start ADCx with DMA request */
if(HAL_ADC_Start_IT(&USER_APPL_ADC1) != HAL_OK)
{
/* ADC initiliazation Error */
Init_Error_Handler();
}
/* Disable the DMA interrupts */
__HAL_DMA_DISABLE_IT(&USER_APPL_DMA3, DMA_IT_HT | DMA_IT_TC );
/* Enables DAC1 and starts conversion of the channel 1 */
if (HAL_DAC_Start(&USER_APPL_DAC1, DAC_CHANNEL_1) != HAL_OK)
{
/* Configuration Error */
Init_Error_Handler();
}
/* Enables DAC3 and starts conversion of the channel 1 */
if (HAL_DAC_Start(&USER_APPL_DAC3, DAC_CHANNEL_1) != HAL_OK)
{
/* Configuration Error */
Init_Error_Handler();
}
/* Start the comparator1 to detect the peak current trip threshold */
if (HAL_COMP_Start(&USER_APPL_CURRENT_SENSE1) != HAL_OK)
{
/* Configuration Error */
Init_Error_Handler();
}
/* Start the comparator3 to detect the peak current trip threshold */
if (HAL_COMP_Start(&USER_APPL_CURRENT_SENSE2) != HAL_OK)
{
/* Configuration Error */
Init_Error_Handler();
}
/* Start the UART2 */
if (HAL_UART_Receive_IT(&huart2, (uint8_t *)&RX2_buff, 8)!= HAL_OK)
{
/* Configuration Error */
Init_Error_Handler();
}
將上述外設(shè)使能后,還要先將串口調(diào)試成功,在程序的調(diào)試過(guò)程中,通過(guò)串口可以將程序運(yùn)行時(shí)的變量傳遞出來(lái)。
本例中,是通過(guò)串口中斷回調(diào)函數(shù)來(lái)完成的。
因?yàn)樵谇懊嬉呀?jīng)通過(guò)函數(shù)
HAL_UART_Receive_IT(&huart2, (uint8_t *)&RX2_buff, 8)
打開(kāi)了串口的接收端口,所以只要串口收到數(shù)據(jù),就會(huì)進(jìn)入上面的回調(diào)函數(shù),進(jìn)行數(shù)據(jù)的處理。如果向外界發(fā)送數(shù)據(jù),用函數(shù)
HAL_UART_Transmit_IT(&huart2, (uint8_t *)&TX2_buff, 5)
至于接收和發(fā)送的內(nèi)容,可以自己填寫(xiě)。
然后將status_machine.c中的StM_Process()狀態(tài)機(jī)的本體移植進(jìn)主循環(huán)中。至于狀態(tài)機(jī)如何運(yùn)行的,可以詳細(xì)閱讀附件中的例程,這里就不再詳述。
下面著重介紹一下斜率補(bǔ)償是如何實(shí)現(xiàn)的。
因?yàn)榉逯惦娏餍涂刂疲枰行甭恃a(bǔ)償。在STM32G474VE這顆芯片中,是通過(guò)HRTIM和DAC相互配合來(lái)實(shí)現(xiàn)的,下圖為斜率補(bǔ)償?shù)漠a(chǎn)生機(jī)制。
其中STRSTDATA[11:0]是由FMAC完成的環(huán)路補(bǔ)償計(jì)算的結(jié)果,用來(lái)更新DAC負(fù)向輸入端的參考值。STRSTTRIG是復(fù)位信號(hào),表示DAC從何時(shí)開(kāi)始產(chǎn)生斜率。STINCTRIG是步長(zhǎng)時(shí)間間隔信號(hào),表示每隔多長(zhǎng)時(shí)間就將STRSTDATA[11:0]的值減去一個(gè)定值。STRSTTRIG與STINCTRIG是HRTIM的關(guān)聯(lián)信號(hào)。
DAC斜率補(bǔ)償?shù)牟介L(zhǎng)時(shí)間,只能通過(guò)TA(或TB...TF)的compare2來(lái)設(shè)定,下圖能清楚的說(shuō)明DAC的斜率補(bǔ)償?shù)墓ぷ髟怼?/p>
然后在DAC1中進(jìn)行設(shè)置。
因?yàn)镽esetData和StepData這兩個(gè)寄存器的值要隨時(shí)根據(jù)環(huán)路計(jì)算值的變化而變化,這里可以先設(shè)為零,在程序中動(dòng)態(tài)修改。
因?yàn)镈AC1是控制TA1和TA2的占空比的,要與TA的時(shí)基同步,所以Trigger和Trigger2要選擇RST Out1 event和STEP Out1 event。
同理,設(shè)置TB和DAC3
斜率補(bǔ)償設(shè)置完成了。因?yàn)镸OS管導(dǎo)通時(shí)會(huì)對(duì)電流互感器的采樣電流產(chǎn)生干擾,進(jìn)而對(duì)comp比較器的輸出產(chǎn)生影響,使環(huán)路出現(xiàn)不穩(wěn)定的狀態(tài),所以有必要進(jìn)行前沿消隱。
對(duì)于TA和TB的前沿消隱,分別用TA-compare3和TB-compare3做為消隱時(shí)間計(jì)數(shù)器。
TB的消隱也是如此設(shè)置。
從上面可以看到TA和TB的前沿消隱配置完成了。