The S32K344 ADC is a SAR ADC with a resolution which can up to 14 bits. It has a variety of software and hardware triggering methods, supports various external trigger sources, and introduces BCTU so that the trigger resources can be externally connected to multi-channel EMIOS and TRIGMUX, adding more ADC trigger sources. This article mainly explains the following ADC software and hardware triggering methods, and provides supporting codes.
Fig 1
It is mainly divided into 5 parts:
(1) SW+ADC: software trigger, by adding timer PIT, the software trigger ADC is called regularly to complete channel sampling, and the collected value is printed out through UART printf.
(2) SW+BCTU+ADC: software trigger, by adding timer PIT, the software trigger BTCU is called regularly to complete ADC channel sampling, and the collected value is printed out through UART printf.
(3) PIT+TRIGMUX+ADC: hardware trigger, connect PIT to ADC through TRIGMUX, trigger ADC channel sampling through PIT hardware, and print the conversion value after the sampling conversion is completed.
(4) EMIOS+BCTUHW+ADC: hardware trigger, through EMIOS timing trigger BCTU to complete the corresponding ADC single channel sampling, due to the high sampling rate, only the BCTU sampling value is printed regularly.
(5) EMIOS+BCTUHWLIST+ADC: hardware trigger, through EMIOS timing trigger BCTU to complete ADC list channel sampling, due to the high sampling rate, the list channel value sampled by BCTU is printed regularly.
2.1 Hardware and software platform
SW: RTD400 LLD,S32DS3.5
HW:S32K3X4EVB-T172
2.2 SW+ADC software trigger
In fact, the original ADC demo of RTD400 already has ADC software and BCTU software trigger. This article adds PIT timing software trigger based on this function, and prints it out through UART printf, making it more convenient to check the ADC test value through serial port printing.
The block diagram structure of the software triggering ADC in this article is as follows:
Fig 2
The S32K344EVB board has a potentiometer connected to ADC1_S10, PTA11:
Fig 3
Therefore, the software trigger in this section is mainly used to collect ADC1_S10. The UART printing port uses the serial port of the onboard emulator: LPUART6_RX PTA15, LPUART6_TX PTA16, with a baud rate of 115200.
For the software trigger demo in this article, the main configuration involves the following modules:
(1)Pins:
Fig 4
ADC1_s10: PTA11 is the voltage of the onboard potentiometer to be tested, which is adjustable.
PTA29: Connect the onboard D13 red light to test the PIT timer interrupt and enter the flashing state, used as the breathing light of the PIT.
PTA16: UART6_TX, used to send the collected ADC value.
(2)clocks
Used to configure the system clock. You need to pay attention to the UART6 clock source of 40Mhz, the ADC1 clock source of 160Mhz, and the PIT0 clock source of 40Mhz
(3)Peripherals
Involved peripheral modules Siul2_Port,Siul2_Dio, Pit, Lpuart_Uart, Adc_Sar_Ip, IntCtrl_Ip.
Siul2_Port: Add 4 pins ADC PTA11 MSCR 11, RED LED PTA29 MSCR 29, UART6_RX PTA15 MSCR 15, UART6_RX PTA16 MSCR 16.
Siul2_Dio: Add the module mainly to allow related API functions to come in, so as to control GPIO pins.
Pit: Used to generate 1S timing, the main configuration is as follows:
Fig 5
Fig 6
Lpuart_Uart:
Fig 7
Adc_Sar_Ip:
Fig 8
Fig 9
It should be noted here that ADC calibration prescale and Adc prescaler vale need to meet the following conditions, which can be found on S32K3RM:
Fig 10
Since the clock source of ADC1 is 160MHz, the calibration division is configured as 4 and the conversion division is configured as 2.
IntCtrl_Ip:
Fig 11
The purpose is to open the interrupt of PIT and LPUART6, and register the corresponding handler.
CT configuration is completed, and the code is generated. Next, move to the main function and add the following code:
void AdcEndOfChainNotif1(void)
{
notif_triggered1 = TRUE;
data1 = Adc_Sar_Ip_GetConvData(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE, 34);
/* Checks the measured ADC data conversion */
// while (ADC_TOLERANCE(data, ADC_BANDGAP));
}
void Pit0ch0Notification(void)
{
toggleLed = 1U;
Siul2_Dio_Ip_TogglePins(LED_Q172_PORT, (1<<LED_Q172_PIN));
}
int main(void)
{
StatusType status;
uint8 Index;
Clock_Ip_StatusType clockStatus;
/* Initialize and configure drivers */
clockStatus = Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
while (clockStatus != CLOCK_IP_SUCCESS)
{
clockStatus = Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
}
Siul2_Port_Ip_Init(NUM_OF_CONFIGURED_PINS_PortContainer_0_BOARD_InitPeripherals, g_pin_mux_InitConfigArr_PortContainer_0_BOARD_InitPeripherals);
/* set PIT 0 interrupt */
IntCtrl_Ip_Init(&IntCtrlConfig_0);
IntCtrl_Ip_EnableIrq(PIT0_IRQn);
status = (StatusType) Adc_Sar_Ip_Init(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE, &AdcHwUnit_1_BOARD_InitPeripherals);
while (status != E_OK);
IntCtrl_Ip_InstallHandler(ADC1_IRQn, Adc_Sar_1_Isr, NULL_PTR);
IntCtrl_Ip_EnableIrq(ADC1_IRQn);
for(Index = 0; Index <= 5; Index++)
{
status = (StatusType) Adc_Sar_Ip_DoCalibration(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE);
if(status == E_OK)
{
break;
}
}
Adc_Sar_Ip_EnableNotifications(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_IP_NOTIF_FLAG_NORMAL_ENDCHAIN | ADC_SAR_IP_NOTIF_FLAG_INJECTED_ENDCHAIN);
/* Initialize PIT instance 0 - Channel 0 */
Pit_Ip_Init(PIT_INST_0, &PIT_0_InitConfig_PB_BOARD_InitPeripherals);
/* Initialize channel 0 */
Pit_Ip_InitChannel(PIT_INST_0, PIT_0_CH_0);
/* Enable channel interrupt PIT_0 - CH_0 */
Pit_Ip_EnableChannelInterrupt(PIT_INST_0, CH_0);
/* Start channel CH_0 */
Pit_Ip_StartChannel(PIT_INST_0, CH_0, PIT_PERIOD);
Lpuart_Uart_Ip_Init(UART_LPUART_INTERNAL_CHANNEL, &Lpuart_Uart_Ip_xHwConfigPB_6_BOARD_INITPERIPHERALS);
printf("S32K344 PIT TRIGMUX ADC demo RTD400.\r\n");
while(1)
{
#if 1
if( toggleLed == 1)
{
toggleLed = 0;
/* Start a SW triggered normal conversion on ADC_SAR */
Adc_Sar_Ip_StartConversion(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_IP_CONV_CHAIN_NORMAL);
/* Wait for the notification to be triggered and read the data */
while (notif_triggered1 != TRUE);
notif_triggered1 = FALSE;
printf("ADC1_s10 ch34 data = %d .\r\n", data1);
}
#endif
}
}
The test results are printed as follows:
Fig 12
This section content supporting code: S32K344_PIT_SW_ADC_RTD400.zip
2.3 SW+BCTU+ADC Software trigger BCTU
Based on SW+ADC trigger, add BCTU, and use BCTU software trigger to complete ADC sampling. The block diagram structure is as follows:
Fig 13
This section uses BCTU software to trigger ADC0 sampling. The sampling channel does not actually use external pin input, but collects the bandgap value of ADC0. The software trigger calls the software trigger function through the PIT 1S cycle, and prints the ADC sampling conversion value to UART after completion.
In the CT tool, the main modification points are peripherals, adding ADC0 in adc_sar_lp, and configuring it as BCTU trigger.
Fig 14
Fig 15
Here we can see that in Figure 14, the adc ctu mode is: trigger mode.
Add the Bctu_Ip module and configure it as follows:
Fig 16
The corresponding selected BCTU channel is 48, which corresponds to the internal bandgap module.
Fig 17
The typical value is 1.2V, so the reference voltage is 5V, and the corresponding 14-bit ADC bandgap expected value is:
(2^14)*1.2/5=3932 around.
After completing the CT configuration code generation, add the following code in main.c:
void AdcEndOfChainNotif(void)
{
notif_triggered = TRUE;
data = Adc_Sar_Ip_GetConvData(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_USED_CH);
}
void Pit0ch0Notification(void)
{
toggleLed = 1U;
Siul2_Dio_Ip_TogglePins(LED_Q172_PORT, (1<<LED_Q172_PIN));
}
void BctuWatermarkNotif(void)
{
uint8 idx;
notif_triggered = TRUE;
for (idx = 0u; idx < BCTU_FIFO_WATERMARK; idx++)
{
data_bctu = Bctu_Ip_GetFifoData(BCTUHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, BCTU_USED_FIFO_IDX);
}
}
int main(void)
{
StatusType status;
uint8 Index;
Clock_Ip_StatusType clockStatus;
/* Initialize and configure drivers */
clockStatus = Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
while (clockStatus != CLOCK_IP_SUCCESS)
{
clockStatus = Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
}
Siul2_Port_Ip_Init(NUM_OF_CONFIGURED_PINS_PortContainer_0_BOARD_InitPeripherals, g_pin_mux_InitConfigArr_PortContainer_0_BOARD_InitPeripherals);
Bctu_Ip_Init(BCTUHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, &BctuHwUnit_0_BOARD_INITPERIPHERALS);
status = (StatusType) Adc_Sar_Ip_Init(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, &AdcHwUnit_0_BOARD_InitPeripherals);
while (status != E_OK);
/* set PIT 0 interrupt */
IntCtrl_Ip_Init(&IntCtrlConfig_0);
IntCtrl_Ip_EnableIrq(PIT0_IRQn);
/* Install and enable interrupt handlers */
IntCtrl_Ip_InstallHandler(ADC0_IRQn, Adc_Sar_0_Isr, NULL_PTR);
IntCtrl_Ip_InstallHandler(BCTU_IRQn, Bctu_0_Isr, NULL_PTR);
IntCtrl_Ip_EnableIrq(ADC0_IRQn);
IntCtrl_Ip_EnableIrq(BCTU_IRQn);
/* Call Calibration function multiple times, to mitigate instability of board source */
for(Index = 0; Index <= 5; Index++)
{
status = (StatusType) Adc_Sar_Ip_DoCalibration(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE);
if(status == E_OK)
{
break;
}
}
Adc_Sar_Ip_EnableNotifications(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_IP_NOTIF_FLAG_NORMAL_ENDCHAIN | ADC_SAR_IP_NOTIF_FLAG_INJECTED_ENDCHAIN);
/* Start a SW triggered conversion on BCTU using a single trigger */
Bctu_Ip_SetGlobalTriggerEn(BCTUHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, TRUE);
Bctu_Ip_EnableNotifications(BCTUHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, BCTU_IP_NOTIF_FIFO1);
/* Initialize PIT instance 0 - Channel 0 */
Pit_Ip_Init(PIT_INST_0, &PIT_0_InitConfig_PB_BOARD_InitPeripherals);
/* Initialize channel 0 */
Pit_Ip_InitChannel(PIT_INST_0, PIT_0_CH_0);
/* Enable channel interrupt PIT_0 - CH_0 */
Pit_Ip_EnableChannelInterrupt(PIT_INST_0, CH_0);
/* Start channel CH_0 */
Pit_Ip_StartChannel(PIT_INST_0, CH_0, PIT_PERIOD);
Trgmux_Ip_Init(&Trgmux_Ip_xTrgmuxInitPB);//
Lpuart_Uart_Ip_Init(UART_LPUART_INTERNAL_CHANNEL, &Lpuart_Uart_Ip_xHwConfigPB_6_BOARD_INITPERIPHERALS);
printf("S32K344 PIT TRIGMUX ADC demo RTD400.\r\n");
while(1)
{
if( toggleLed == 1)
{
toggleLed = 0;
Bctu_Ip_SwTriggerConversion(BCTUHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, BCTU_USED_SINGLE_TRIG_IDX);
while (notif_triggered != TRUE);
notif_triggered = FALSE;
printf("ADC0_bandgap ch48 data_bctu = %d .\r\n", data_bctu);
}
}
}
Test result:
Fig 18
It is close to the typical expected value, indicating that it has been successfully run.
Used demo code:S32K344_PIT_TRIGMUX_BCTUSW_ADC_printf_RTD400.zip
2.4 PIT+TRIGMUX+ADC hardware PIT TRIGUMX trigger
This section is about hardware triggering. PIT is used in combination with TRIGMUX to directly trigger ADC1 channel 34, i.e. ADC1_S10 sampling. The trigger structure diagram is as follows:
Fig 19
Also based on the previous code, you need to add an additional module Trgmux_Ip in the CT peripherals, and the rest of the configuration remains unchanged.
Fig 20
Here, the input of Trigmux is selected as PIT0_CH0 and the output is ADC1.
The code is also much simpler. Add the following code in main:
Trgmux_Ip_Init(&Trgmux_Ip_xTrgmuxInitPB);//
while(1)
{
if(notif_triggered1 == TRUE)
{
notif_triggered1 = FALSE;
printf("ADC1_s10 ch34 data = %d .\r\n", data1);
}
}
In While(1), we can see that there is no software-triggered call. We can directly check the ADC1 conversion completion flag and then print the data. The test results are as follows:
Fig 21
It can be seen that as the external potentiometer changes, the sampled value of ADC1_S10 also changes.
Used demo:S32K344_PIT_TRIGMUX_ADC_printf_RTD400.zip
2.5 EMIOS+BCTUHW+ADC hardware EMIOS BCTU trigger
The block diagram structure of this section is as follows:
Fig 22
Use eMIOS0_CH0 to generate a 10Khz clock to trigger BCTU to complete the sampling of ADC0_48 channel, that is, bandgap.
In the CT tool, add Emios_Mcal_Ip and configure it as follows:
Fig 23
Change the BCTU configuration to enable HW triggering. The configuration is as follows:
Fig 24
Main code related codes are as follows:
Emios_Mcl_Ip_Init(EMIOS_INST0, &Emios_Mcl_Ip_0_Config_BOARD_INITPERIPHERALS);
while(1)
{
if( toggleLed == 1)
{
toggleLed = 0;
printf("ADC0_bandgap ch48 data_bctu = %d .\r\n", data_bctu);
}
}
Since the sampling rate is triggered at a frequency of 10Khz, the frequency is relatively fast, so the printing here is still based on 1s. The printing results are as follows:
Fig 25
As you can see, the result is also a variable bandgap value.
Used demo:S32K344_PIT_TRIGMUX_BCTUHW_EMIOS_ADC_printf_RTD400.zip
2.6 EMIOS+BCTUHW LIST+ADC hardware EMIOS BCTU trigger LIST
This section is similar to the EMIOS BCTU hardware trigger above, except that the BCTU is configured in the form of LIST, which can trigger the conversion of multiple channels at once. The main modifications are in the BCTU module:
Fig 26
Add the corresponding main code as follows:
#define BCTU_FIFO_WATERMARK 3U
void BctuWatermarkNotif(void)
{
uint8 idx;
notif_triggered = TRUE;
for (idx = 0u; idx < BCTU_FIFO_WATERMARK; idx++)
{
data_bctu[idx] = Bctu_Ip_GetFifoData(BCTUHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, BCTU_USED_FIFO_IDX);
}
}
while(1)
{
if( toggleLed == 1)
{
toggleLed = 0;
printf("ADC0_bandgap ch48 data_bctu = %d .\r\n", data_bctu[0]);
printf("ADC0_vrefl ch54 data_bctu = %d .\r\n", data_bctu[1]);
printf("ADC0_vrefh ch55 data_bctu = %d .\r\n", data_bctu[2]);
}
}
Test result is:
Fig 27
It can be seen that the results are consistent with the collected bandgap, VREFL, and VREFH, indicating that the code function is running normally.
Code in this section:S32K344_PIT_TRIGMUX_BCTUHWLIST_EMIOS_ADC_printf_RTD400.zip