Preface#
If you have not read the author's previous articles "Step One Configuration" and "MSPM0 Tutorial on Calling SYSCONFIG and Configuring GPIO Output and Input," it is strongly recommended that you read these two articles first to avoid configuration errors. If you have completed the SYSCONFIG configuration, please continue reading.
Chuangyi Stack MSPM0 Development Board Reference Voltage Selection#
According to the user manual of Chuangyi Stack, the onboard VREF is 2.5V. We just need to switch the dip switch on the board to ON.
SYSCONFIG Configuration#
Open empty.syscfg in Keil, then call the SYSCONFIG tool.
In SYSCONFIG, we will add ADC and first take a look at its introduction.
In the basic configuration, users can:
- Configure the sampling clock and sampling mode: These parameters affect the ADC's sampling rate, which is the number of times the input signal is sampled per second.
- Configure ADC conversion memory: Store the digital data obtained from the conversion of the analog signal.
- Enable/disable the following functions:
- Hardware averaging mode: Reduces the impact of random noise by averaging multiple sampling results.
- Use a burned current source: May be used to provide a stable reference voltage during ADC conversion.
- Window comparator mode: ADC conversion only occurs when the input signal is within a specified range.
- Trigger mode: ADC conversion can be set to start only upon receiving a specific trigger signal.
In the advanced configuration, users can:
- Configure conversion frequency/resolution: Affects the accuracy and speed of ADC conversion.
- Enable FIFO mode: In this mode, the ADC can continuously perform multiple conversions and store the results in a first-in-first-out (FIFO) queue for subsequent processing.
- Configure power-down mode: The power of the ADC module can be reduced when ADC conversion is not needed to save energy.
- Specify sampling time: For rapidly changing analog signals, shorter sampling times may be required.
ADC Parameters#
As shown in the reference manual, the conversion cycle of this ADC at different resolutions.
If we use ULPCLK, we can bypass this sampling delay.
Check the repeated sampling mode.
Configure the trigger source in SYSCONFIG. I set it to event-triggered, with the event published by the timer.
In the Conversion Data Format option, select unsigned right-aligned.
In the advanced settings, we can determine the sampling rate by setting the acquisition time (the upper limit for 12bit is 1.45M). The sampling rate equals 32 (clock frequency)/(1/sampling time + conversion time (12bit resolution is 14 clock cycles)). By calculation, setting the sampling time to 250ns (i.e., eight (32M) clock cycles), the sampling rate will be 32/(8+14)=1.45M.
Next, we will configure the timer. The timer configuration is shown in the figure; ensure that the corresponding trigger event is set correctly.
Then return to the ADC configuration page and set up the subscription event.
Then enable DMA transfer and set the DMA parameters, including trigger source, data length, address mode settings, and transfer mode.
The transfer mode defines how data is transferred from the source address to the destination address. There are four modes:
-
Single Transfer: In this mode, each transfer requires a trigger signal. After the number of transfers set by "Transfer Size" is completed, the DMA will be disabled.
-
Block Transfer: In this mode, a single trigger signal can transfer a complete block of data, with the block size determined by "Transfer Size." The DMA will be disabled after each transfer.
-
Repeated Single Transfer: In this mode, each transfer requires a trigger signal, but the DMA remains enabled after all transfers are completed.
-
Repeated Block Transfer: In this mode, a single trigger signal can transfer a complete block of data, with the block size determined by "Transfer Size." Like "Repeated Single Transfer," the DMA remains enabled after all transfers are completed.
The choice of transfer mode depends on your specific needs. For example, if you need to continuously transfer large amounts of data, you can choose "Block Transfer" or "Repeated Block Transfer" mode; if you need fine control over each data transfer, you can choose "Single Transfer" or "Repeated Single Transfer" mode.
Click FILE-save, then compile in Keil and import the configuration.
Main Related Code#
#define ADCSize 128
typedef enum ADCFlag
{
ADCFlagDmaStart = 0,
ADCFlagDmaDone,
ADCFlagConversionDone,
ADCFlagConversionStart
}ADCFlag;
unsigned short ADCResult[ADCSize];// Define ADC reception buffer
ADCFlag adcflag;// Define enumeration variable adcflag
void UART1SendString(char *str)
{
while(*str != '\0')
{
DL_UART_transmitDataBlocking(UART1_INST, *str);
str++;
}
}
void UART1SendChar(char c)
{
DL_UART_transmitDataBlocking(UART1_INST, c);
}
void strreverse(char* begin, char* end) {
char aux;
while(end>begin)
aux=*end, *end--=*begin, *begin++=aux;
}
/**
* @brief Converts an integer value to a null-terminated string of a specified base and stores the result in the array given by the str parameter.
*
* @param value: The value to convert to a string.
* @param str: The array for storing the result as a null-terminated string. This array should be long enough to hold the result string (including the null character).
* @param base: The base of the number to convert. It must be in the range [2, 36]. For bases greater than 10, this function uses lowercase letters.
*
* @note If the base is 10 and value is negative, the result string will be prefixed with a negative sign (-). For any other base, value is always treated as unsigned.
*
* @note If the base is not in the range [2, 36], the function will perform no operations, and str will be an empty string.
*
* @note This function does not handle "unsigned long" numbers - it will treat them as "long." Therefore, for values greater than 2147483647, this function does not work for bases greater than 10.
*
* @return None.
*/
void itoa(int value, char* str, int base) {
static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz";
char* wstr = str;
int sign;
// Validate base
if (base < 2 || base > 35) { *wstr = '\0'; return; }
// Take care of sign
if ((sign=value) < 0) value = -value;
// Conversion. Number is reversed.
do *wstr++ = num[value%base]; while(value/=base);
if(sign<0) *wstr++='-';
*wstr='\0';
// Reverse string
strreverse(str,wstr-1);
}
/**
* @brief Function to process ADC results
*
* @param adcflag: Pointer to the ADC flag, which will be set to ADCFlagDmaDone when the ADC completes data conversion
* @param ADCResult: Pointer to the array storing ADC results
* @param resultSize: Size of the ADC result array
*
* @note When the value pointed to by adcflag is ADCFlagDmaDone, the function stops the timer and ADC data conversion, then iterates through the ADCResult array,
* and uses the itoa function to convert each result to a string, which is then sent via UART. After sending all results, the function sets the value pointed to by adcflag to ADCFlagConversionStart,
* indicating that the ADC can start a new round of data conversion. Note that when sending strings, a newline character is sent after each result so that the receiving end can correctly identify the boundaries of each result.
*
* @return None.
*/
void processADCResults(ADCFlag* adcflag, unsigned short* ADCResult, int resultSize)
{
if (*adcflag == ADCFlagDmaDone)
{
DL_Timer_stopCounter(TIMER_0_INST);
DL_ADC12_stopConversion(ADC12_0_INST);
volatile int i;
for ( i = 0; i < resultSize; i++)
{
char buffer[12]; // Buffer for integer to string conversion, max size for int32 is 11 characters + null terminator
itoa(ADCResult[i], buffer, 10);
UART1SendString(buffer);
UART1SendString("\r\n");
}
*adcflag = ADCFlagConversionStart;
i = 0;
}
}
void setupDMA(DMA_Regs *dma, uint8_t channelNum, unsigned int srcAddr, unsigned int destAddr, unsigned int transferSize)
{
DL_DMA_setSrcAddr(dma, channelNum, (unsigned int) srcAddr);
DL_DMA_setDestAddr(dma, channelNum, (unsigned int) destAddr);
DL_DMA_setTransferSize(dma, channelNum, transferSize);
DL_DMA_enableChannel(dma, channelNum);
}
int main(void)
{
SYSCFG_DL_init();
NVIC_EnableIRQ(UART1_INT_IRQn);
NVIC_EnableIRQ(ADC0_INT_IRQn);
adcflag = ADCFlagConversionStart;
setupDMA(DMA,DMA_CH0_CHAN_ID,(unsigned int) & (ADC0->ULLMEM.MEMRES[0]),(unsigned int) &ADCResult,ADCSize);
if(adcflag == ADCFlagConversionStart)
{
DL_Timer_startCounter(TIMER_0_INST);
DL_ADC12_startConversion(ADC12_0_INST);
}
while (1)
{
processADCResults(&adcflag, ADCResult, ADCSize);
}
}
Actual Demonstration#
Since I only sample 128 points each time and do not sample continuously, each time I reset, the sampled signal will definitely not be continuous with the last time, resulting in the sharp points shown below. From the perspective of a continuous signal, the effect is still quite good.
Please indicate the source when reprinting.
by <a href=https://www.cnblogs.com/jiongsheng > QDU_jiongsheng