Wiznet makers

ronpang

Published June 09, 2025 © Apache License 2.0 (Apache-2.0)

146 UCC

81 WCC

34 VAR

0 Contests

1 Followers

0 Following

Chapter 19: W55MH32’s ADC - Voltage Acquisition

Chapter 19: W55MH32’s ADC - Voltage Acquisition

COMPONENTS
PROJECT DESCRIPTION

Chapter 19: W55MH32’s ADC - Voltage Acquisition

References for this chapter: "W55MH32 Reference Manual" - ADC section. When studying this chapter, it will be more effective to read it in conjunction with the ADC section of the "W55MH32 Reference Manual", especially for the part related to register explanations.

1 Introduction to ADC

The 12-bit ADC is a successive approximation type analog-to-digital converter. It has up to 18 channels and can measure 16 external and 2 internal signal sources. The A/D conversion of each channel can be performed in single-shot, continuous, scanning or intermittent modes. The results of the ADC can be stored in a 16-bit data register in either left-aligned or right-aligned format. The analog watchdog feature enables the application to detect whether the input voltage exceeds the user-defined high/low thresholds.

The input clock of the ADC should not exceed 14 MHz and is generated by dividing PCLK2.

2 Analysis of the ADC Function Block Diagram

The block diagram of the ADC function is as follows:

descript

The rule conversion and injection conversion triggers of ADC3 are different from those of ADC1 and ADC2.

 

Name

Signal type

Annotation

VREF+

Input, analog reference positive electrode

The high-end / positive reference voltage used by ADC, 2.4V ≤ VREF+ ≤ VDDA

VDDA⁽¹⁾

Input, Analog Power Supply

Equivalent to the analog power supply of VDD and: 2.4V ≤ VDDA ≤ VDD (3.6V)

VREF-

Input, analog reference negative electrode

The low-end/negative reference voltage used by ADC, VREF-, is equal to VSSA.

VSSA⁽¹⁾

Input, analog power ground

Equivalent to the analog power ground of VSS

ADCx_IN[15:0]

Simulated input signal

16 analog input channels

VDDA and VSSA should be respectively connected to VDD and VSS.

2.1 Voltage Input Range

The input range of ADC is: VREF- ≤ VIN ≤ VREF+. This is determined by the four external pins: VREF-, VREF+, VDDA, and VSSA.

When designing the schematic diagram, we usually ground VSSA and VREF-, and connect VREF+ and VDDA to 3V3. Thus, the input voltage range of the ADC becomes 0 to 3.3V.

If we want to widen the input voltage range to be able to test negative voltages or higher positive voltages, we can add an external voltage regulation circuit. We can raise or lower the voltage to be converted to 0 to 3.3V. In this way, the ADC can measure.

2.2 Input Channel

After we have determined the input voltage for the ADC, how is the voltage fed into the ADC? Here, we introduce the concept of channels. The ADC of W55MH32 has up to 18 channels, among which the 16 external channels are ADCx_IN0, ADCx_IN1... ADCx_IN5 as shown in the block diagram. These 16 channels correspond to different I/O ports, and which specific I/O port can be found in the manual. Among them, the internal channels ADC1/2/3 are also connected: Channel 16 of ADC1 is connected to the internal temperature sensor, and Vrefint is connected to channel 17. The analog channels 16 and 17 of ADC2 are connected to the internal VSS. The analog channels 9, 14, 15, 16 and 17 of ADC3 are connected to the internal VSS.

ADC1

IO

ADC2

IO

ADC3

IO

Passage 0

PA0

Passage0

PA0

Passage 0

PA0

Passage 1

PA1

Passage1

PA1

Passage1

PA1

Passage 2

PA2

Passage2

PA2

Passage2

PA2

Passage3

PA3

Passage3

PA3

Passage3

PA3

Passage 4

PA4

Passage4

PA4

Passage4

No passage4

Passage 5

PA5

Passage5

PA5

Passage5

No passage5

Passage 6

PA6

Passage6

PA6

Passage6

No passage6

Passage 7

PA7

Passage7

PA7

Passage7

No passage7

Passage 8

PB0

Passage8

PB0

Passage8

No passage8

Passage 9

PB1

Passage9

PB1

Passage9

Connect the internal VSS

Passage10

PC0

Passage10

PC0

Passage10

PC0

Passage11

PC1

Passage11

PC1

Passage11

PC1

Passage12

PC2

Passage12

PC2

Passage12

PC2

Passage13

PC3

Passage13

PC3

Passage13

PC3

Passage14

PC4

Passage14

PC4

Passage14

Connect the internal VSS

Passage15

PC5

Passage15

PC5

Passage15

Connect the internal VSS

Passage16

Connect the internal temperature sensor

Passage16

Connect internallyVSS

Passage16

Connect the internal VSS

Passage17

Connect to the internal Vrefint

Passage17

Connect internally VSS

Passage17

Connect the internal VSS

The 16 external channels are divided into regular channels and injection channels during the conversion process. Among them, the regular channels can have up to 16 channels, while the injection channels can have up to 4 channels. What are the differences between these two types of channels? And when should they be used?

 

Rule Channel

Rule Channel: As the name suggests, the rule channel simply means being very orderly. The channel we usually use is this one, or we can say that all the channels we employ are this one. There is nothing particularly noteworthy to mention.

 

Injection channel

Injection can be understood as insertion or cutting in. It is a disorderly passage. It is a way of forcibly inserting a certain passage into the one that is about to be converted when there is a transition from one rule-based passage to another. If during the transition from one rule-based passage to another, there is an injection passage cutting in, then the injection passage must be converted first. After the injection passage is converted, one should return to the conversion process of the rule-based passage. This is similar to interrupting a program; both are manifestations of disorderliness. Therefore, the injection passage will only appear when the rule-based passage exists.

2.3 Conversion Order

Rule sequence

There are three rule sequence registers, namely SQR3, SQR2, and SQR1. SQR3 controls the first to the sixth transformations in the rule sequence, and the corresponding bits are: SQ1[4:0]~SQ6[4:0]. The first transformation is bit 4:0 SQ1[4:0]. If channel 16 wants to perform the first transformation, then write 16 in SQ1[4:0]. SQR2 controls the seventh to the twelfth transformations in the rule sequence, and the corresponding bits are: SQ7[4:0]~SQ12[4:0]. If channel 1 wants to perform the eighth transformation, then write 1 in SQ8[4:0]. SQR1 controls the thirteenth to the sixteenth transformations in the rule sequence, and the corresponding bits are: SQ13[4:0]~SQ16[4:0]. If channel 6 wants to perform the tenth transformation, then write 6 in SQ10[4:0]. The specific number of channels to be used is determined by the bits L[3:0] of SQR1, and the maximum number of channels is 16.

Register

Register bit

Function

Value

SQR3

SQ1[4:0]

Set the channel for the first conversion

Channels 1 to 16

SQR3

SQ2[4:0]

Set up the second conversion channel

Channels 1 to 16

SQR3

SQ3[4:0]

Set up the third conversion channel

Channels 1 to 16

SQR3

SQ4[4:0]

Set the fourth conversion channel

Channels 1 to 16

SQR3

SQ5[4:0]

Set the fifth conversion channel

Channels 1 to 16

SQR3

SQ6[4:0]

Set up the 6th conversion channel

Channels 1 to 16

SQR3

SQ7[4:0]

Set the 7th conversion channel

Channels 1 to 16

SQR3

SQ8[4:0]

Set the 8th conversion channel

Channels 1 to 16

SQR2

SQ9[4:0]

Set the 9th conversion channel

Channels 1 to 16

SQR2

SQ10[4:0]

Set the 10th conversion channel

Channels 1 to 16

SQR2

SQ11[4:0]

Set the 11th conversion channel

Channels 1 to 16

SQR2

SQ12[4:0]

Set the 12th conversion channel

Channels 1 to 16

SQR1

SQ13[4:0]

Set the 13th conversion channel

Channels 1 to 16

SQR1

SQ14[4:0]

Set the 14th conversion channel

Channels 1 to 16

SQR1

SQ15[4:0]

Set the 15th conversion channel

Channels 1 to 16

SQR1

SQ16[4:0]

Set the 16th conversion channel

Channels 1 to 16

SQR1

LQL[3:0]

How many channels need to be converted?

 1 to 16

Injection sequence

There is only one injection sequence register JSQR, and it can support up to 4 channels. The exact number is determined by the JL[1:0] of JSQR. If the value of JL is less than 4, then the setting for the conversion order determined by JSQR is different from that of SQR. The first conversion is not JSQR1[4:0], but JCQRx[4:0], where x = 4 - JL), which is exactly the opposite of SQR. If JL = 00 (1 conversion), then the conversion order starts from JSQR4[4:0], not from JSQR1[4:0]. This should be noted, and be careful not to make mistakes when programming. When JL is equal to 4, it is the same as SQR.

Register

Register bit

Function

Value

JSQR

JSQ1[4:0]

Set the channel for the first conversion

Channels 1 to 4

JSQR

JSQ2[4:0]

Set up the second conversion channel

Channels 1 to 4

JSQR

JSQ3[4:0]

Set up the third conversion channel

Channels 1 to 4

JSQR

JSQ4[4:0]

Set the fourth conversion channel

Channels 1 to 4

JSQR

JLQ[4:0]

How many channels need to be converted?

1 to 4

 

2.4 Trigger Source

The channel has been selected and the conversion sequence has been set. Now it's time to start the conversion. The conversion of the ADC can be controlled through the ADC control register 2 (i.e., ADC_CR2). The ADON bit is used to control the start (when it is set to 1) and stop (when it is set to 0) of the conversion. This is the simplest and most understandable method for implementing ADC conversion.

In addition to the above control methods, ADC also supports triggering conversion. This triggering includes internal timer triggering and external IO triggering. There are many triggering sources, and which triggering source to select is controlled by the EXTSEL[2:0] and JEXTSEL[2:0] bits in ADC control register 2 (i.e., ADC_CR2). EXTSEL[2:0] is used to select the triggering source of the rule channel, while JEXTSEL[2:0] is used to select the triggering source of the injection channel. After selecting the triggering source, whether to activate this triggering source is controlled by the EXTTRIG and JEXTTRIG bits in ADC control register 2 (i.e., ADC_CR2).

2.5 Conversion Time

ADC clock

The ADC input clock ADC_CLK is generated by dividing PCLK2. The maximum frequency is 14M. The division factor is set by the bits 15:14 of the RCC clock configuration register RCC_CFGR, which can be 2/4/6/8 divisions. Note that there is no 1 division. Usually, we set PCLK2 = HCLK = 72M.

 

Sampling time

The ADC samples the input voltage using several ADC_CLK cycles. The number of sampling cycles can be set by the SMP[2:0] bits in the ADC_SMPR1 and ADC_SMPR2 registers. ADC_SMPR2 controls channels 0 to 9, while ADC_SMPR1 controls channels 10 to 17. Each channel can be sampled at a different time. The minimum sampling period is 1.5 cycles, which means if we want the fastest sampling, the sampling period should be set to 1.5 cycles. Here, the cycle refers to 1/ADC_CLK.

The conversion time of the ADC is related to the ADC input clock and sampling time. The formula is: Tconv = Sampling time + 12.5 cycles. When ADCLK = 14 MHz (the highest), the sampling time is set to 1.5 cycles (the fastest), then the total conversion time (the shortest) Tconv = 1.5 cycles + 12.5 cycles = 14 cycles = 1 us.

Generally, we set PCLK2 = 72 MHz. After passing the ADC pre-divider, the maximum clock that can be divided down is only 12 MHz. The sampling period is set to 1.5 cycles. The shortest conversion time calculated is 1.17 us, which is the most commonly used.

2.6 Data register

After all preparations are completed, the data converted by ADC is placed in the ADC_DR register according to the different conversion groups, and the data of the injection group is placed in JDRx.

 

Rule data register

The ADC rule group data register ADC_DR has only one, which is a 32-bit register. The lower 16 bits are used in a single ADC, while the upper 16 bits store the rule data from ADC2 conversion in dual-mode ADC1. Dual mode means that ADC1 and ADC2 are used simultaneously. In single mode, ADC1/2/3 do not use the upper 16 bits. Because the ADC accuracy is 12 bits, neither the upper 16 nor the lower 16 bits of ADC_DR can fully accommodate the data, and they can only be stored in a left-aligned or right-aligned manner. Which way to store depends on the 11th bit of ADC_CR2.

There can be up to 16 rule channels. However, there is only one rule data register. If multiple channels are used for conversion, all the conversion data will be stored in DR. The data from the channel converted at the previous time point will be overwritten by the data from another channel converted at the next time point. Therefore, when the channel conversion is completed, the data should be retrieved or the DMA mode should be enabled to transfer the data to the memory. Otherwise, data will be overwritten. The most common practice is to enable DMA transmission.

 

Inject data register

The ADC injection group can have a maximum of 4 channels, and exactly 4 data registers are used for injection. Each channel corresponds to its own register, and there will be no problem of data overwrite like in the regular registers. ADC_JDRx is 32 bits in size, with the lower 16 bits being valid and the upper 16 bits being reserved. The data is also divided into left-aligned and right-aligned formats, and which one is stored is determined by the 11th bit of ADC_CR2's ALIGN setting.

2.7 Interruption

Conversion termination interruption

After the data conversion is completed, an interruption can occur. Interruptions are divided into three types: the interruption when the rule channel conversion is completed, the interruption when the injection conversion channel is completed, and the interruption of the analog watchdog. Among them, the interruption when the conversion is completed is quite understandable. It is similar to the interruptions we are accustomed to, with corresponding interrupt flags and interrupt enable bits. We can also write corresponding interrupt service programs based on the interrupt type.

 

Simulated watchdog interrupt

When the analog voltage converted by the ADC is lower than the low threshold or higher than the high threshold, an interrupt will occur. The prerequisite is that we have enabled the analog watchdog interrupt. The low threshold and high threshold are set by ADC_LTR and ADC_HTR. For example, if we set the high threshold to 2.5V, then when the analog voltage exceeds 2.5V, an analog watchdog interrupt will occur. The same applies to the low threshold.

 

DMA request

After the rules and channel injection conversion are completed, in addition to generating an interrupt, a DMA request can also be generated to directly store the converted data in the memory. It should be noted that only ADC1 and ADC3 can generate a DMA request. Regarding the DMA request, it needs to be studied in conjunction with the "W55MH32 Reference Manual" - the DMA Controller section. Generally, when using ADC, we will enable DMA transmission.

2.8 Voltage Conversion

After the analog voltage is converted by the ADC, it becomes a 12-bit digital value. If this value is printed out via serial port in hexadecimal format, its readability is relatively poor. Therefore, sometimes we need to convert the digital voltage into an analog voltage. We can also compare it with the actual analog voltage (measured with a multimeter) to check if the conversion is accurate.

When designing the schematic diagram, we usually set the input voltage range of the ADC to be 0 to 3.3V. Since the ADC is 12-bit, the full-scale range of 12 bits corresponds to 3.3V. The digital value corresponding to the full-scale range of 12 bits is: 2^12. The value 0 corresponds to 0V. If the converted value is X, and the corresponding analog voltage is Y, then the following equation holds: 2^12 / 3.3 = X / Y, => Y = (3.3 * X) / 2^12.

 

3 Detailed Explanation of ADC Initialization Structure

The standard library functions establish an initialization structure xxx_InitTypeDef (where xxx represents the name of the peripheral) for each peripheral. The members of this structure are used to set the working parameters of the peripheral, and the standard library function xxx_Init() calls these settings to enter the corresponding registers of the peripheral, achieving the purpose of configuring the working environment of the peripheral.

The collaborative working mechanism between the structure xxx_InitTypeDef and the library function xxx_Init() reflects the core idea of modular design. The structure xxx_InitTypeDef is defined in the file w55mh32_xxx.h, and the library function xxx_Init() is defined in the file w55mh32_xxx.c. During programming, we can use these two files along with their comments.

 

ADC_InitTypeDef structure

The ADC_InitTypeDef structure is defined in the w55mh32_adc.h file. Its specific definition is as follows:

typedef struct
{
    uint32_t ADC_Mode;                 
    FunctionalState ADC_ScanConvMode;       
                                           
    FunctionalState ADC_ContinuousConvMode; 
    uint32_t ADC_ExternalTrigConv;         
    uint32_t ADC_DataAlign;               
    uint8_t ADC_NbrOfChannel;             
} ADC_InitTypeDef;
 

ADC_Mode: Configures the mode of the ADC. When using a single ADC, it is in the independent mode. When using two ADCs, it is in the dual mode. In the dual mode, there are many sub-modes available for selection. We usually use the independent mode of a single ADC.

 

ScanConvMode: The available options are ENABLE and DISABLE. This parameter determines whether scanning is used. If it is a single-channel AD conversion, DISABLE should be selected; if it is a multi-channel AD conversion, ENABLE should be chosen.

 

ADC_ContinuousConvMode: The optional parameters are ENABLE and DISABLE. The configuration determines whether to enable automatic continuous conversion or a single conversion. Using the ENABLE configuration enables automatic continuous conversion; using the DISABLE configuration enables a single conversion, which stops after one conversion and requires manual control to restart the conversion. The default setting is usually for continuous conversion.

 

ADC_ExternalTrigConv: The parameters are used to configure the selection mechanism of the external trigger source for the analog-to-digital converter. This configuration item provides multiple programmable trigger modes, including hardware trigger sources such as timer output signals and external pin events, as well as a software autonomous trigger mode. In practical engineering applications, considering the simplification of system design and improvement of trigger accuracy, the software automatic trigger (SWSTART) method is usually adopted to achieve conversion control. This mode can precisely start the conversion process by directly calling library functions, effectively avoiding the risk of external signal interference.

 

ADC_DataAlign: Conversion result data alignment mode, which can be either right-aligned (ADC_DataAlign_Right) or left-aligned (ADC_DataAlign_Left). Generally, we choose the right-aligned mode.

 

ADC_NbrOfChannel: Number of AD conversion channels. It can be set according to actual requirements.

4 Single-channel ADC Conversion

4.1 Code Analysis

1. Header files and macro definitions

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "delay.h"
#include "w55mh32.h"
#include "math.h"
 
#define CONV_CHANNEL_NUM 1
#define VREF            (3300)
 
#define ADC_TEST_CHANNEL_PIN (GPIO_Pin_6)
uint8_t ADC_CovChannel[1] = {ADC_Channel_6};
uint8_t ADC_SampleTIME[1] = {ADC_SampleTime_239Cycles5};
uint32_t DAM_ADC_Value[1];

Header files: Standard library header files and custom header files were introduced, such as stdlib.h, string.h, stdio.h, etc. for the standard library, and delay.h and w55mh32.h are custom header files, and math.h is the header file for the mathematics library.

Macro definitions:

CONV_CHANNEL_NUM: Defines the number of ADC conversion channels, here it is 1 channel.

VREF: Reference voltage value, in units of mV, here it is 3300 mV.

ADC_TEST_CHANNEL_PIN: Defines the GPIO pin corresponding to the ADC test channel, it is GPIO_Pin_6.

Arrays:

ADC_CovChannel: Stores the ADC channels to be converted, here it is ADC_Channel_6.

ADC_SampleTIME: Stores the sampling time for each channel, here it is ADC_SampleTime_239Cycles5.

DAM_ADC_Value: Used to store the ADC conversion results transported by DMA.

 

2. Function Declaration

void UART_Configuration(void);
void ADC_Configuration(void);
void DMA_Configuration(void);

 

Declared the functions used for configuring the serial port, ADC and DMA.

 

3. The main() function

int main(void)
{
    RCC_ClocksTypeDef clocks;
    delay_init();
 
    UART_Configuration();
    RCC_GetClocksFreq(&clocks);
 
    printf("\n");
    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
           (float)clocks.SYSCLK_Frequency / 1000000, (float)clocks.HCLK_Frequency / 1000000,
           (float)clocks.PCLK1_Frequency / 1000000, (float)clocks.PCLK2_Frequency / 1000000, (float)clocks.ADCCLK_Frequency / 1000000);
 
    printf("ADC Single Test\n");
 
    ADC_Configuration();
 
    while (1)
    {
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);
        delay_ms(1000);
    }
}
 

 

 

Initialize the delay function delay_init().

Call UART_Configuration() to configure the serial communication.

Obtain the system clock frequency and output it via the serial port.

Call ADC_Configuration() to configure the ADC.

Enter an infinite loop and trigger the conversion of ADC1 once per second through software.

 

4. GetCmd() Function

uint8_t GetCmd(void)
{
    uint8_t tmp = 0;
 
    if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
    {
        tmp = USART_ReceiveData(USART1);
    }
    return tmp;
}
 

This function is used to receive data from the serial port. If data is received by the serial port 1, it will be read and returned.

 

5. UART_Configuration() Function

void UART_Configuration(void)
{
    //GPIO port settings
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
 
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    USART_InitStructure.USART_BaudRate            = 115200;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
 
    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}
 

 

Enable the clocks for USART1 and GPIOA.

Configure Pin_9 of GPIOA as a re-mapped push-pull output for serial port transmission; Pin_10 as an open-drain input for serial port reception.

Configure the parameters such as baud rate, data bits, stop bits, and parity bit for USART1, and enable USART1.

 

6. ADC_Configuration() Function

void ADC_Configuration(void)
{
    uint32_t         i;
    ADC_InitTypeDef  ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
 
    GPIO_InitStructure.GPIO_Pin   = ADC_TEST_CHANNEL_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);
    ADC_DeInit(ADC1);
 
    ADC_InitStructure.ADC_Mode               = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode       = ENABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv   = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign          = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel       = CONV_CHANNEL_NUM;
    ADC_Init(ADC1, &ADC_InitStructure);
 
    for (i = 0; i < CONV_CHANNEL_NUM; i++)
    {
        ADC_RegularChannelConfig(ADC1, ADC_CovChannel[i], i + 1, ADC_SampleTIME[i]);
    }
 
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
 
    ADC_Cmd(ADC1, ENABLE);
 
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));
 
    DMA_Configuration();
}

 

Enable the clocks for GPIOA and ADC1.

Configure GPIOA_Pin_6 as an analog input mode.

Configure the clock of ADC1, reset ADC1.

Configure the working mode, scanning mode, continuous conversion mode, etc. of ADC1.

Configure the rule channels of ADC1.

Start an ADC conversion, enable ADC1.

Perform a calibration operation on ADC1.

Call DMA_Configuration() to configure DMA.

 

 

7. DMA_Configuration() Function

void DMA_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    DMA_InitTypeDef  DMA_InitStructure;
 
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)DAM_ADC_Value;
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize         = CONV_CHANNEL_NUM;
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Word;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    DMA_Cmd(DMA1_Channel1, ENABLE);
    ADC_DMACmd(ADC1, ENABLE);
 
    NVIC_InitStructure.NVIC_IRQChannel                   = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE);
}

 

 

Enable the clock for DMA1, and reset DMA1 channel 1.

Configure the source address (ADC1's data register), destination address (DAM_ADC_Value array), transfer direction, buffer size, etc. parameters of DMA.

Enable DMA1 channel 1 and the DMA function of ADC1.

Configure the interrupt priority of DMA1 channel 1 and enable the interrupt.

 

8. DMA1_Channel1_IRQHandler() function

void DMA1_Channel1_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC1) != RESET)
    {
        DMA_ClearITPendingBit(DMA1_IT_TC1);
        DMA_ClearFlag(DMA1_FLAG_TC1);
 
        printf("Code Value = %d ,voltage value = %2.4f\n", DAM_ADC_Value[0],
               (float)VREF * DAM_ADC_Value[0] / 4095 / 1000);
    }
}

 

When the interrupt occurs due to the completion of DMA1 channel 1 transmission, clear the interrupt flag and the corresponding flag bit.

Output the digital value and the corresponding voltage value obtained from the ADC through the serial port.

 

9. The SER_PutChar() and fputc() functions

//Retarget Printf
int SER_PutChar(int ch)
{
    while (!USART_GetFlagStatus(USART1, USART_FLAG_TC));
    USART_SendData(USART1, (uint8_t)ch);
 
    return ch;
}
 
int fputc(int c, FILE *f)
{
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART */
    if (c == '\n')
    {
        SER_PutChar('\r');
    }
    return (SER_PutChar(c));
}

 

These two functions are used to redirect the printf() function, allowing the output of printf to be sent out via the serial port.

4.2 Download verification

This code implements the single-channel analog signal acquisition for ADC_Channel_6. It uses DMA to transfer the ADC conversion results to the designated array. The converted digital values and corresponding voltage values are output via serial port. The conversion is performed once per second. The following are the conversion results:

descript

5. Dual ADC Synchronous Rule Mode Acquisition Experiment

The AD conversion process consists of the sampling stage and the conversion stage. Data from the channels is only collected during the sampling stage; during the conversion stage, the collected data is merely converted into digital quantities for output. At this point, changes in the channel data do not affect the conversion result.

In the independent mode, ADC acquisition requires collecting data from one channel and completing the conversion before proceeding to the next channel. The dual ADC mechanism uses two ADCs to simultaneously sample one or more channels. The biggest advantage of the dual ADC mode over the independent mode is that it increases the sampling rate, compensating for the insufficient sampling speed of a single ADC.

When enabling the dual ADC mode, by configuring the DUALMOD[3:0] bits of the ADC_CR1 register, several different modes can be selected. Please refer to the following table for details. Summary of various modes of the dual ADC mode:

Mode

Brief explanation

Synchronous injection mode

ADC1 and ADC2 simultaneously convert an injection channel group, with ADC1 as the master and ADC2 as the slave. The converted data is stored in the ADC_JDRx register of each ADC interface.

Synchronization rule mode

ADC1 and ADC2 simultaneously convert a set of standard channels, with ADC1 as the master and ADC2 as the slave. The conversion results of ADC1 are placed in the lower 16 bits of ADC1_DR, while the conversion results of ADC2 are placed in the upper 16 bits of ADC1_DR.

Fast crossover mode

ADC1 and ADC2 alternate to collect a regular channel group (usually one channel). After ADC2 is triggered, ADC1 needs to wait for 7 ADCCLK cycles before it can be triggered.

Slow-speed crossing mode

ADC1 and ADC2 alternate to collect a regular channel group (which can only consist of one channel). After ADC2 is triggered, ADC1 needs to wait for 14 ADCCLK cycles before it can be triggered.

Alternate triggering mode

ADC1 and ADC2 take turns to collect the injection channel groups. After all the channels of ADC1 have been collected, ADC2's channels will be collected next, and this process repeats in a cycle. This is different from cross-collection.

Mixed rules / Injection synchronous mode

The synchronous conversion of the rule group was interrupted to initiate the synchronous conversion of the injection group. To understand it, separate the two modes. The difference is that the injection group can interrupt the conversion of the rule group.

Mixed synchronization rules + Alternating trigger mode

The synchronous conversion of the rule group was interrupted, and the alternate triggering conversion of the injection group was initiated. Understanding it separately in terms of the two modes is sufficient. The difference is that the injection group can interrupt the conversion of the rule group.

Mixed synchronous injection + Cross mode

Cross conversion can be interrupted by the synchronous injection mode. At this point, cross conversion is halted and injection conversion is initiated.

Here, we will use the synchronous rule mode as the example for the explanation. The synchronous rule mode is that ADC1 and ADC2 simultaneously convert a rule channel group. ADC1 is the master and ADC2 is the slave. The conversion result of ADC1 is placed in the lower 16 bits of ADC1_DR, and the conversion result of ADC2 is placed in the upper 16 bits of ADC1_DR. And the DMA function must be enabled.

The external trigger comes from the rule group multiplexer of ADC1 (selected by the EXTSEL[2:0] bits in the ADC1_CR2 register), which simultaneously provides synchronous trigger to ADC2. For simplicity, we choose software trigger for both ADC1 and ADC2.

For the simplicity of the experiment, in the experiment, we select each of ADC1 and ADC2 to collect one channel:

descript

5.1 Code Analysis

1. Header files and macro definitions

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "delay.h"
#include "w55mh32.h"
#include "math.h"
 
#define CONV_CHANNEL_NUM 1
#define VREF            (3300)

 

Header file:

It includes the standard C library header files as well as custom header files, which are used to implement basic library function calls, delay    functionality, etc.

Macro definition:

CONV_CHANNEL_NUM: Defines the number of ADC conversion channels. Here, it is set to 1, indicating that each ADC uses one channel for conversion.

VREF: Defines the reference voltage value. The unit is mV. Here, it is set to 3300 mV, which is used to convert the digital value obtained from ADC conversion to the actual voltage value later.

 

 

2.Function declaration

void UART_Configuration(void);
void ADC_Configuration(void);
void DMA_Configuration(void);

 

Three functions are declared, each used for configuring serial communication, ADC module and DMA module.

 

3. Global Variables

uint32_t DAM_ADC_Value[1];

 

A global array named DAM_ADC_Value has been defined, which is used to store the conversion results transferred from the data register of ADC1 by DMA.

 

4. The main() function

int main(void)
{
    RCC_ClocksTypeDef clocks;
    delay_init();
 
    UART_Configuration();
    RCC_GetClocksFreq(&clocks);
 
    printf("\n");
    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
           (float)clocks.SYSCLK_Frequency / 1000000, (float)clocks.HCLK_Frequency / 1000000,
           (float)clocks.PCLK1_Frequency / 1000000, (float)clocks.PCLK2_Frequency / 1000000, (float)clocks.ADCCLK_Frequency / 1000000);
 
    printf("ADC Double Test(ADC1 & ADC2)\n");
 
    ADC_Configuration();
 
    while (1)
    {
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);
        delay_ms(1000);
    }
}

 

 

Initialization:

The delay function `delay_init()` is initialized for subsequent delay operations.

The `UART_Configuration()` function is called to configure the serial communication for subsequent output of information through the serial port.

The system clock frequency information is obtained and output through the serial port for convenient debugging and viewing of the system clock     configuration.

A prompt message is output to indicate the start of the ADC dual-mode test.

The `ADC_Configuration()` function is called to configure the ADC module.

Main loop:

The ADC1 is triggered by software to start the conversion operation.

A delay of 1 second is performed to control the frequency of ADC conversion.

5. GetCmd() Function

uint8_t GetCmd(void)
{
    uint8_t tmp = 0;
 
    if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
    {
        tmp = USART_ReceiveData(USART1);
    }
    return tmp;
}

 

 

This function is used to receive data from the serial port. If data is received by Serial Port 1 (the USART_FLAG_RXNE flag is set), then the data is read and returned.

6. UART_Configuration() Function

void UART_Configuration(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
 
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    USART_InitStructure.USART_BaudRate            = 115200;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
 
    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}

 

 

 

Clock Enable: Enable the clocks for USART1 and GPIOA, as USART1 uses the pins of GPIOA for communication.

GPIO Configuration:

Configure GPIOA's Pin 9 as a re-mapped push-pull output mode for the USART1's transmission function.

Configure GPIOA's Pin 10 as an open-drain input mode for the USART1's reception function.

USART Configuration:

Set the baud rate of USART1 to 115200, with 8 data bits, 1 stop bit, no parity bit, no hardware flow control, and support for both transmit and receive modes.

Initialize USART1 and enable this module.

 

7. ADC_Configuration() Function

void ADC_Configuration(void)
{
    ADC_InitTypeDef  ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);
 
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);
    ADC_DeInit(ADC1);
    ADC_DeInit(ADC2);
 
    ADC_InitStructure.ADC_Mode               = ADC_Mode_RegSimult; //synchronization rule mode
    ADC_InitStructure.ADC_ScanConvMode       = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv   = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign          = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel       = 1;
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_Init(ADC2, &ADC_InitStructure);
 
    /* channel configuration */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
 
    /* module enablement */
    ADC_Cmd(ADC1, ENABLE);
    ADC_Cmd(ADC2, ENABLE);
 
    /* adc calibration */
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));
    ADC_ResetCalibration(ADC2);
    while (ADC_GetResetCalibrationStatus(ADC2));
    ADC_StartCalibration(ADC2);
    while (ADC_GetCalibrationStatus(ADC2));
 
    DMA_Configuration();
}

 

Clock Enable: Enable the clocks for GPIOA, ADC1, and ADC2.

GPIO Configuration: Configure GPIOA's Pin 0 and Pin 1 as analog input modes, respectively, for ADC1's channel 0 and ADC2's channel 1.

ADC Clock Configuration and Reset: Configure the ADC's clock and divide PCLK2 by 8 as the ADC's clock source. Reset ADC1 and ADC2 and restore their registers to default states.

ADC Initialization:

● Set the ADC operation mode to ADC_Mode_RegSimult, which is the rule synchronous mode, allowing ADC1 and ADC2 to simultaneously perform conversions on their respective channels.

● Disable the scanning mode and continuous conversion mode and use software to trigger conversions.

● Right-align the data, with each ADC performing a conversion using 1 channel.

● Initialize ADC1 and ADC2 separately.

Channel Configuration:

● Configure ADC1's rule channel as channel 0, with a conversion sequence of 1 and a sampling time of 239.5 cycles.

● Configure ADC2's rule channel as channel 1, with a conversion sequence of 1 and a sampling time of 239.5 cycles.

Enable ADC Module: Enable ADC1 and ADC2.

ADC Calibration: Reset and start the calibration operations for ADC1 and ADC2, and wait for the calibration to complete.

Call DMA Configuration Function: Call the DMA_Configuration() function to configure the DMA module.

 

8. DMA_Configuration() Function

void DMA_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    DMA_InitTypeDef  DMA_InitStructure;
 
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)DAM_ADC_Value;
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize         = CONV_CHANNEL_NUM;
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Word;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    DMA_Cmd(DMA1_Channel1, ENABLE);
    ADC_DMACmd(ADC1, ENABLE);
 
    NVIC_InitStructure.NVIC_IRQChannel                   = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE);
}
 

 

Clock enable and DMA reset:

Enable the clock for DMA1.

Reset channel 1 of DMA1.

DMA initialization:

Set the source address of DMA to the data register of ADC1, ADC1->DR.

Set the destination address of DMA to the DAM_ADC_Value array.

The data transfer direction is from the peripheral (ADC1's data register) to the memory.

The buffer size is CONV_CHANNEL_NUM, which is 1.

The peripheral address does not increment, while the memory address increments.

The data transfer size is a word (32 bits).

The DMA operation mode is circular, meaning it automatically restarts after completing one transmission.

Set the DMA priority to high.

Disable the memory-to-memory transfer mode.

Initialize DMA1's channel 1 and enable this channel.

Enable the DMA function of ADC1.

NVIC configuration:

Configure the interrupt priority of DMA1 channel 1 with both preemption priority and sub-priority set to 0.

Enable the interrupt of DMA1 channel 1.

Enable transfer completion interrupt for DMA1 channel 1.

9. DMA1_Channel1_IRQHandler() function

void DMA1_Channel1_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC1) != RESET)
    {
        DMA_ClearITPendingBit(DMA1_IT_TC1);
        DMA_ClearFlag(DMA1_FLAG_TC1);
 
        printf("ADC1 Code Value = %d ,voltage value = %2.4f\n", DAM_ADC_Value[0] & 0xFFFF,
               (float)VREF * (DAM_ADC_Value[0] & 0xFFFF) / 4095 / 1000);
        printf("ADC2 Code Value = %d ,voltage value = %2.4f\n", (DAM_ADC_Value[0] >> 16) & 0xFFFF,
               (float)VREF * ((DAM_ADC_Value[0] >> 16) & 0xFFFF) / 4095 / 1000);
    }
}

 

 

Interrupt Handling:

When the transmission completion interrupt of DMA1 channel 1 occurs, clear the interrupt flag and the corresponding flag bit.

Data Processing and Output:

Extract the conversion results of ADC1 and ADC2 from DAM_ADC_Value[0]. The result of ADC1 is stored in the low 16 bits, and the result of ADC2 is stored in the high 16 bits.

Convert the extracted digital values to corresponding voltage values, and output the digital values and voltage values of ADC1 and ADC2 through the serial port.

 

10. SER_PutChar and fputc() functions

//Retarget Printf
int SER_PutChar(int ch)
{
    while (!USART_GetFlagStatus(USART1, USART_FLAG_TC));
    USART_SendData(USART1, (uint8_t)ch);
 
    return ch;
}
 
int fputc(int c, FILE *f)
{
    if (c == '\n')
    {
        SER_PutChar('\r');
    }
    return (SER_PutChar(c));
}

 

 

SER_PutChar() function: Sends a character to USART1 and waits for the transmission to complete.

fputc() function: Redirects the output of printf() function to USART1. When a newline character '\n' is output, it first sends a carriage return character '\r' to ensure correct display of the newline on the terminal.

This code configures ADC1 and ADC2 to simultaneously collect analog signals from different channels in the rule synchronization mode, uses DMA to transfer the conversion results to the memory, and finally outputs the converted digital values and corresponding voltage values through the serial port, achieving the function of ADC dual-mode testing.

 

5.2 Download Verification

descript

 

6 ADC Analog Watchdog Function

6.1 Code Analysis

1. Header files and global declarations

w55mh32.h: Contains definitions of STM32 peripheral registers (such as ADC1, USART1).

delay.h: Provides millisecond-level delay function: delay_ms().

 

2. main() function: Initialization and main loop

int main(void) {
   
    delay_init();         
    UART_Configuration(); 
    ADC_Configuration();   
 
   
    printf("SYSCLK: %3.1fMhz...\n", (float)clocks.SYSCLK_Frequency / 1000000);
 
   
    while (1) {
        ADC_SoftwareStartConvCmd(ADC1, ENABLE); 
        delay_ms(200);                           
    }
}

3. ADC_Configuration: ADC and Analog Watchdog Configuration

void ADC_Configuration(void) {
   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
 
   
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);
 
   
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_Cmd(ADC1, ENABLE); 
 
   
    NVIC_Init(&NVIC_InitStructure);
 
   
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5);
 
   
    ADC_AnalogWatchdogThresholdsConfig(ADC1, 2048, 1024); 
    ADC_AnalogWatchdogSingleChannelConfig(ADC1, ADC_Channel_10); 
    ADC_AnalogWatchdogCmd(ADC1, ADC_AnalogWatchdog_SingleRegEnable); 
 
   
    ADC_ResetCalibration(ADC1);
    ADC_StartCalibration(ADC1);
}

 

 

 

4. Interrupt handling function: ADC1_2_IRQHandler()

void ADC1_2_IRQHandler(void) {
    ADC_ITConfig(ADC1, ADC_IT_AWD, DISABLE); 
 
    if (ADC_GetFlagStatus(ADC1, ADC_FLAG_AWD)) { 
        ADC_ClearFlag(ADC1, ADC_FLAG_AWD);       
        printf("ADC Awd is Happened. Code Value = %d \r\n", ADC1->DR); 
    }
 
    ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE); 
}
 

5. printf redirection: Serial port output

This code implements the ADC analog watchdog function of W55MH32, and outputs system information and voltage overrun alarms via the serial port. The core process: configure ADC and serial port → periodically trigger ADC conversion → monitor voltage threshold → interrupt response. It is suitable for scenarios that require real-time monitoring of analog signals (such as battery voltage monitoring, sensor input protection).

6.2 Download Verification

descript

 

7 ADC_VrefintTemper

7.1 Code Analysis

1. Global Definitions and Variables

#define CONV_CHANNEL_NUM 2         
#define VREF            (3300)   
uint8_t ADC_CovChannel[2] = {ADC_Channel_16, ADC_Channel_17}; 
uint32_t DAM_ADC_Value[2]; 

 

 

2. main() function: Initialization and main loop

int main(void) {
    delay_init();         
    UART_Configuration(); 
    ADC_Configuration();   
 
   
    printf("SYSCLK: %3.1fMhz...\n", (float)clocks.SYSCLK_Frequency / 1000000);
 
   
    while (1) {
        ADC_SoftwareStartConvCmd(ADC1, ENABLE); 
        delay_ms(1000);                         
    }
}

3. ADC_Configuration: ADC Initialization

void ADC_Configuration(void) {
   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
 
   
    RCC_ADCCLKConfig(RCC_PCLK2_Div4);
 
   
    ADC_InitStructure.ADC_ScanConvMode = ENABLE; 
    ADC_InitStructure.ADC_NbrOfChannel = CONV_CHANNEL_NUM; 
    ADC_Init(ADC1, &ADC_InitStructure);
 
 
    for (i=0; i<CONV_CHANNEL_NUM; i++) {
        ADC_RegularChannelConfig(ADC1, ADC_CovChannel[i], i+1, ADC_SampleTIME[i]);
    }
 
 
    ADC_TempSensorVrefintCmd(ENABLE);
 
   
    ADC_ResetCalibration(ADC1);
    ADC_StartCalibration(ADC1);
 
   
    DMA_Configuration();
}

 

 

 

4. DMA_Configuration()DMA Initialization

void DMA_Configuration(void) {
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; 
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DAM_ADC_Value; 
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 
    DMA_InitStructure.DMA_BufferSize = CONV_CHANNEL_NUM; 
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    DMA_Cmd(DMA1_Channel1, ENABLE);
 
 
    ADC_DMACmd(ADC1, ENABLE);
 
        NVIC_Init(&NVIC_InitStructure);
    DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE); 
}

 

 

5. DMA interrupt handling function:DMA1_Channel1_IRQHandler()

void DMA1_Channel1_IRQHandler(void) {
    if (DMA_GetITStatus(DMA1_IT_TC1)) { 
        DMA_ClearITPendingBit(DMA1_IT_TC1);
 
        // Calculate and print the result (unit: mV)
        printf("temperature AD: %d → %.4fV\n", DAM_ADC_Value[0], 
               (float)VREF * DAM_ADC_Value[0] / 4095 / 1000);
        printf("VREFINT AD: %d → %.4fV\n", DAM_ADC_Value[1], 
               (float)VREF * DAM_ADC_Value[1] / 4095 / 1000);
    }
}

 

This code utilizes the internal ADC channels (temperature sensor and VREFINT) of the W55MH32, combined with DMA, to achieve efficient data acquisition. It outputs real-time voltage values through the serial port. It is suitable for scenarios that require monitoring internal sensors or calibrating ADCs (such as industrial control and embedded system diagnosis). Core advantages: DMA reduces CPU load, internal sensors do not require external wiring, and continuous collection in a loop mode.

7.2 Download Verification

descript

 

WIZnet is a fabless semiconductor company founded in 1998. Its products include the Internet processor iMCU™, which utilizes TOE (TCP/IP Offload Engine) technology and is based on a unique patented fully hardwired TCP/IP. The iMCU™ is designed for embedded Internet devices in various applications.

WIZnet has more than 70 distributors globally and has offices in Hong Kong, South Korea, and the United States, providing technical support and product marketing services.

The regions managed by the Hong Kong office include Australia, India, Turkey, and Asia (excluding South Korea and Japan). 

Documents
Comments Write