【RTD200P04 MCAL 篇3】 S32M244 PWM PDB ADC控制
一,文档简介
S32M2XX系列是专门为电机控制而生的MCU系列:
S32M24X,延续S32K14X系列MCU,添加了AE预驱模块。
S32M27X,延续了S32K3系列MCU,添加了AE预驱模块。
NXP官方提供了S32M24X和S32M27X系列的EVB,可以配合一个三相PMSM/BLDC电机,实现单电阻电流采样方式的电机控制。研究电机控制的必经之路是要懂得怎么去控制PWM,实现在特定时刻采集电流,电压信号,并且经过计算再回调PWM控制。本文将基于S32M24X-EVB:
https://www.nxp.com/design/design-center/development-boards-and-designs/S32M24XEVB
讲解如何实现在S32M244上,输出带有死区的互补PWM,并且使用FTM_INT作为触发信号,通过PDB延迟触发ADC采样,将ADC采集的值通过UART打印到PC串口,另外通过TRGMUX模块实现不同模块的相互连接,比如FTM和PDB,还有将信号通过TRGMUX_OUT引脚输出。实现的框图结构如下:PWM+TRGMUX+PDB+ADC 双通道。本文主要是使用MCAL的方式实现功能。
本人做的两个配套MCAL附件代码:
S32DSMCAL: Mcal_PWMPDBADC_S32M244_RTD200P04.zip
GCCMCAL: PWMPDBADC_TS_T40D2M20I0R0_kerry.zip
GCCMCAL,是使用ARM GCC makefile方式直接编译,可以参考工具篇的vscode方式去编译生成elf文件去烧录。
S32DSMCAL,是将EB MCAL工程集成到S32DS的工程中去的方式。
本文的主要目的是分享小编自己做的S32M244 MCAL PWM PDB ADC demo,因为目前还没有直接的对应的集成的M244 MCAL demo,如果熟悉电机控制的,也可以直接参考官方S32M244电机控制LLD demo自行去做,但是目前M244电机控制还没有MCAL demo。
二,PWM+TRGMUX+PDB+ADC 2ch 软件配置与实现
2.1 软硬件版本平台
硬件:S32M244-EVB
软件:
- S32DS : S32DS.3.5_b220726_win32.x86_64.exe
- Update 4 for S32DS:SW32_S32DS_3.5.4_D2307.zip
- Development package for S32K1XX: SW32K1_S32DS_3.5.4_D2307.zip
- Development package for S32M2XX: SW32M2xx_S32DS_3.5.0_D2303.zip
- RTD:
S32K1_S32M24X Real Time Drivers AUTOSAR 4.4 & R21-11 Version 2.0.0
S32K1_S32M24X Real Time Drivers AUTOSAR R21-11 Version 2.0.0 P04 - S32M24x_AMMCLIB_RTM_1_1_39_BIN
- S32M24XEVB Evaluation Board Motor Control Application Software :
S32M24XEVB-SW.exe - FreeMASTER tool 3.2 : FMASTERSW32.exe
- VSCode
- EB tresos studio 29.0
这里的软件平台是能够让电机demo都运行起来的这个平台, 如果不需要电机,只需要装S32DS, update,development,RTD,EB即可, VScode取决于个人工具使用习惯,酌情安装。
具体的软件链接,建议直接到官网对应软件下载的地方去搜索下载对应版本即可。
2.2 MCAL工程以及模块配置
本文对应了两种类型的MCAL 工程:
(1)直接在SW32K1_S32M24x_RTD_R21-11_2.0.0_P04现有的MCAL工程,拷贝一个,然后修改EB MCAL的配置,并且添加代码到main,通过makefile方式去编译生成elf,然后再去烧录elf的方式。
(2)新建S32DS工程,然后集成SW32K1_S32M24x_RTD_R21-11_2.0.0_P04驱动,以及EB生成的代码,再添加main代码,这种方式就可以直接在S32DS里面编译,并且烧录的方式。
关于两种方式的编译以及构建,小编有其他对应的工具篇文章详细讲解,自行查阅。
S32K3 工具篇6:如何将RTD EB工程导入到S32DS
S32K3 工具篇7:如何使用VScode编译EB MCAL工程
S32K3 工具篇8:如何移植RTD MCAL现有demo到其他K3芯片
对于EB工程,本例程使用到的相关模块如下:
BaseNXP, Dem, EcuC 模块属于基础模块,没有特别需要配置的,下面对重点模块具体讲解:
2.2.1 Dio 模块配置
Dio主要用到PTE15作为LED引脚,对于PTE15,Port_Id=4, Dio Channel Id=15.
Port_Id, A=0, B=1,C=2,D=3,E=4…,Dio Channel Id是对应port中的引脚号。
2.2.2 Adc模块配置
Adc模块需要做ADC模块的配置,比如传输类型,分频系数,校准分频系数,转换分辨率,PDB硬件模块配置情况,ADC通道,ADC group,group里面要配置引脚触发源,通道延迟情况等,具体相关配置如下:
(1)AdcHwUnit
AdcHwUnit_0->General
主要几点,12位分辨率,然后PDB周期是8000,因为PDB用的是系统时钟80Mhz,所以是10K周期的PDB counter。
AdcHwUnit_0->AdcChannel
AdcChannel定义了两个通道,分别为SE11_ADCH11外部连接板载的可调电位器,还有一个是BANDGAP_ADCH27,是内部的bandgap 1V。
AdcHwUnit_0->AdcGroup
AdcGroup里面需要配置多个地方:
AdcHwUnit_0->AdcGroup0->General: 转换模式,触发模式,采样时间等。
AdcHwUnit_0->AdcGroup0->AdcChannelDelay:定义两个通道PDB触发延迟的时间,必须要满足单通道能够转换完的时间,否则会造成PDB错误。
AdcHwUnit_0->AdcGroup0->AdcGroupDefinition:定义group对应的通道情况
(2) AdcHwTrigger
(3) AdcInterrupt
2.2.3 Mcu模块配置
Mcu模块主要用于配置MCU的相关时钟McuClockSettingConfig,工作模式McuModeSettingConf.
McuClockSettingConfig时钟配置选项较多,不一一展开,可以参考附件代码。
这里需要注意下McuclockReferencePoint
这里涉及到FTM,ADC, PDB, UART模块的时钟参考源情况。
2.2.4 Platform模块配置
Platform配置主要是Interrupt controller的配置:
添加对应模块的中断号和中断Handler。
2.2.5 Port模块配置
Port中配置了使用到的引脚端口,主要是PortContainer的配置,当前配置情况如下:
2.2.6 Pwm模块配置
Pwm模块配置有两个点:
(1)PwmChannel
(2) PwmFtm
PwmFtm_0->Ftm Modules
这里配置了边沿对齐模式,死区的时间为0.5us。
PwmFtm_0->Ftm Sync
PwmFtm_0->PwmFtmCh
配置为Combined Mode,互补输出,FTM0通道0和1.
2.2.7 Uart模块配置
Uart->UartChannel
2.3主程序调用情况
配置完成之后,可以通过在main中调用如下代码实现PWM触发PDB采集ADC双通道的情况,PWM可以输出互补信号,并且还可以实现相关的触发信号通过TRGMUX_OUT引脚输出:
#ifdef __cplusplus
extern "C" {
#endif
#include "Mcu.h"
#include "Port.h"
#include "Dio.h"
#include "Pwm.h"
#include "Platform.h"
#include "Adc.h"
#include "Mcl.h"
#include "Lpuart_Uart_Ip_Irq.h"
#include "CDD_Uart.h"
#include <string.h>
#include "stdio.h"
#include "retarget.h"
#include "check_example.h"
#define RESULT_ADC_BANDGAP (1241U) /* Vbandgap ~ 1.0V at 3.3V reference */
#define NUM_RESULTS (4u)
volatile uint32 VarNotification_0 = 0;
volatile uint32 pdberrcnt = 0;
extern ISR(Adc_0_Isr);
Adc_ValueGroupType ResultBuffer[NUM_RESULTS] = {0xaaaa, 0xaaaa,0xaaaa, 0xaaaa};
/* Used for ReadGroup */
Adc_ValueGroupType AdcReadGroupBuffer[NUM_RESULTS] = {0xbbbb, 0xbbbb,0xbbbb, 0xbbbb};
//UART
#define UART_LPUART_INTERNAL_CHANNEL 0U
#define WELCOME_MSG "Helloworld for automotive with S32M244!\r\n"
#define MSG_LEN 50U
void AdcNotification(void)
{
VarNotification_0++;
}
void PDB_ADC_SEQ_ERR_NOTIFICATION()
{
pdberrcnt ++;
}
void TestDelay(uint32 delay);
void TestDelay(uint32 delay)
{
static volatile uint32 DelayTimer = 0;
while(DelayTimer<delay)
{
DelayTimer++;
}
DelayTimer=0;
}
int main(void)
{
uint8 count = 0U;
Std_ReturnType StdReturn = E_NOT_OK;
volatile boolean bStatus = TRUE;
Adc_CalibrationStatusType CalibStatus;
volatile Std_ReturnType T_Uart_Status1;
/* Initialize the Mcu driver */
#if (MCU_PRECOMPILE_SUPPORT == STD_ON)
Mcu_Init(NULL_PTR);
#elif (MCU_PRECOMPILE_SUPPORT == STD_OFF)
Mcu_Init(&Mcu_Config_VS_0);
#endif /* (MCU_PRECOMPILE_SUPPORT == STD_ON) */
/* Initialize the clock tree and apply PLL as system clock */
Mcu_InitClock(McuClockSettingConfig_0);
#if (MCU_NO_PLL == STD_OFF)
while ( MCU_PLL_LOCKED != Mcu_GetPllStatus() )
{
/* Busy wait until the System PLL is locked */
}
Mcu_DistributePllClock();
#endif
Mcu_SetMode(McuModeSettingConf_0);
/* Initialize all pins using the Port driver */
Port_Init(NULL_PTR);
Platform_Init(NULL_PTR);
Platform_InstallIrqHandler(LPUART0_RxTx_IRQn, LPUART_UART_IP_0_IRQHandler, NULL_PTR);
Pwm_Init(&Pwm_Config_VS_0);
Mcl_Init(&Mcl_Config_VS_0);
Adc_Init(&Adc_Config_VS_0);
Uart_Init(NULL_PTR);
/* Part 1: Example with SW Triggered One-Shot Conversion Mode, data conversion is updated by Interrupt.*/
/*******************************************************************************************************/
Adc_Calibrate(AdcHwUnit_0, &CalibStatus);
if(CalibStatus.AdcUnitSelfTestStatus == E_NOT_OK)
{
bStatus = FALSE;
}
/* ResultBuffer is updated new data in Adc_0_Isr handler */
Adc_SetupResultBuffer(AdcGroup_0, ResultBuffer);
Adc_EnableGroupNotification(AdcGroup_0);
//for ADC hardware
Adc_EnableHardwareTrigger(AdcHwUnit_0);
Adc_StartGroupConversion(AdcHwUnit_0);
printf("S32M244 FTM TRIGMUX ADC demo RTD200P04!\r\n");
while (1)
{
printf("===========New Round ADC sampling=========\r\n");
/* Get input level of channels */
Dio_WriteChannel(DioConf_DioChannel_DioChannel_LEDD20, STD_HIGH);
TestDelay(2000000);
TestDelay(2000000);
Pwm_SetDutyCycle(PwmChannel_0, 0x2000);
Dio_WriteChannel(DioConf_DioChannel_DioChannel_LEDD20, STD_LOW);
TestDelay(2000000);
TestDelay(2000000);
Pwm_SetDutyCycle(PwmChannel_0, 0x6000);
VarNotification_0 = 0;
while(VarNotification_0 == 0)
{}
StdReturn = Adc_ReadGroup(AdcGroup_0, AdcReadGroupBuffer);
printf("adc1_SE11 = %d \r\n",(int)AdcReadGroupBuffer[0]);
printf("adc1_bandGap = %d \r\n",(int)AdcReadGroupBuffer[1]);
}
Exit_Example(TRUE);
return (0U);
}
#ifdef __cplusplus
}
#endif
三, 测试结果
测试结果包括两部分:打印结果看ADC采样值情况,以及PWM输出和PDB触发位置的关系情况。打印结果如下,可以看到ADC的两个通道的值是正确的:
PWM波形测试情况如下:
CH1: PTC0
CH2: PTC1
CH3: PTD0
CH4: PTD1
CH5: PTE11