前言
如果您未看过笔者之前写的"第一步配置"和"MSPM0教程之调用SYSCONFIG以及配置GPIO输出和输入",强烈建议您将这两篇先看了,以免出现配置错误,如果您以及完成了SYSCONFIG的配置,请继续往下看
创易栈MSPM0开发板参考电压选择
根据创易栈的用户手册,板载VREF2.5V,我们把板子上的拨码开关拨到ON即可
SYSCONFIG配置
在keil内打开empty.syscfg,然后调用SYSCONFIG工具
进入SYSCONFIG,我们再添加ADC,先看看他的介绍
在基本配置中,用户可以:
- 配置采样时钟和采样模式:这些参数影响了ADC的采样率,即每秒钟对输入信号进行采样的次数。
- 配置ADC转换内存:存储从模拟信号转换得到的数字数据。
- 启用/禁用以下功能:
- 硬件平均模式:通过对多次采样结果进行平均,减少随机噪声的影响。
- 使用烧耗电流源:可能用于在ADC转换过程中提供稳定的参考电压。
- 窗口比较器模式:只有当输入信号在一个指定的范围内时,才进行ADC转换。
- 触发模式:可以设置ADC转换只在接收到一个特定的触发信号时开始。
在高级配置中,用户可以:
- 配置转换频率/分辨率:影响了ADC转换的精度和速度。
- 启用FIFO模式:在这种模式下,ADC可以连续进行多次转换,并将结果存入一个先进先出(FIFO)的队列中,方便后续处理。
- 配置电源下降模式:在不需要进行ADC转换时,可以降低ADC模块的电源,以节省能源。
- 指定采样时间:对于快速变化的模拟信号,可能需要更短的采样时间。
ADC参数
如参考手册所示,该ADC的在不同分辨率下的转换周期
如果我们使用ULPCLK,可以绕过这个采样延迟
勾选重复采样模式
在SYSCONFIG中配置ADC的触发源,我设置为事件触发,这个事件由定时器发布
Conversion Data Format这个选项中选择无符号右对齐
在高级设置这里,我们可以通过设置采集的时间来确定采样率(12bit上限为1.45M),采样率等于F(时钟频率)/( (F*t(采样时间)) + T(转换时间,12bit分辨率 为14个时钟周期)),通过计算可以得到,将采样时间设置为250ns(即八个(32M)时钟周期),此时采样率就为32/(8+14)=1.45M
上述计算过程如有问题敬请指正
注意
如果使用MSPM0G的话,这里的转换时间并不相同,应按照数据手册的时钟频率与转换时间关系表确定
然后我们去配置定时器,定时器配置如图,确保设置好对应的触发事件
接着再回到ADC配置页,设置好订阅事件和触发模式
然后打开DMA传输,设置好DMA的参数,包括触发源,数据长度和传输地址模式设置以及传输模式
传输模式定义了数据如何从源地址传输到目标地址,有四种模式:
-
Single Transfer:这种模式下,每次传输都需要一个触发信号。当按照"Transfer Size"设定的次数完成传输后,DMA将被禁用。
-
Block Transfer:这种模式下,一个触发信号就能传输完整的数据块,数据块的大小由"Transfer Size"决定。每次传输结束后,DMA将被禁用。
-
Repeated Single Transfer:这种模式下,每次传输都需要一个触发信号,但在所有传输完成后,DMA仍然保持启用状态。
-
Repeated Block Transfer:这种模式下,一个触发信号就能传输完整的数据块,数据块的大小由"Transfer Size"决定。和"Repeated Single Transfer"一样,所有传输完成后,DMA仍然保持启用状态。
选择哪种传输模式取决于你的具体需求。例如,如果你需要连续传输大量数据,可以选择"Block Transfer"或"Repeated Block Transfer"模式;如果你需要精细地控制每次的数据传输,可以选择"Single Transfer"或"Repeated Single Transfer"模式。
点击FILE-save,然后到keil中编译,导入配置
main相关代码
#define ADCSize 128
typedef enum ADCFlag
{
ADCFlagDmaStart = 0,
ADCFlagDmaDone,
ADCFlagConversionDone,
ADCFlagConversionStart
}ADCFlag;
unsigned short ADCResult[ADCSize];//定义ADC接收缓冲区
ADCFlag adcflag;//定义枚举变量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 将整数值转换为指定基数的空终止字符串,并将结果存储在由str参数给出的数组中。
*
* @param value: 要转换为字符串的值。
* @param str: 用于存储结果的空终止字符串的数组。该数组应足够长,以容纳结果字符串(包括空字符)。
* @param base: 要转换的数字的基数。它必须在[2, 36]范围内。对于大于10的基数,此函数使用小写字母。
*
* @note 如果基数为10且value为负,则结果字符串前会带有负号(-)。对于任何其他基数,value始终被视为无符号。
*
* @note 如果基数不在[2, 36]范围内,函数将不执行任何操作,str将是一个空字符串。
*
* @note 该函数不处理"unsigned long"数字 - 它将把它们视为"long"。因此,对于大于2147483647的值,该函数不适用于大于10的基数。
*
* @return 无。
*/
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 处理ADC结果的函数
*
* @param adcflag: 指向ADC标志的指针,当ADC完成数据转换后,该标志会被设置为ADCFlagDmaDone
* @param ADCResult: 指向存储ADC结果的数组的指针
* @param resultSize: ADC结果数组的大小
*
* @note 当adcflag指向的值为ADCFlagDmaDone时,函数会停止定时器和ADC的数据转换,然后遍历ADCResult数组,
* 并使用itoa函数将每个结果转换为字符串,然后通过UART发送出去。在发送完所有的结果后,函数会将adcflag指向的值设置为ADCFlagConversionStart,
* 表示ADC可以开始新一轮的数据转换。注意,在发送字符串时,每个结果后面都会发送一个换行符,以便接收端能够正确地识别每个结果的边界。
*
* @return 无。
*/
void processADCResults(ADCFlag* adcflag, unsigned short* ADCResult, int resultSize)
{
if (*adcflag == ADCFlagDmaDone)
{
DL_Timer_stopCounter(TIMER_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);
}
while (1)
{
processADCResults(&adcflag, ADCResult, ADCSize);
}
}
void ADC0_IRQHandler (void)
{
switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST))
{
case DL_ADC12_IIDX_DMA_DONE:
/* code */
adcflag = ADCFlagDmaDone;
DL_ADC12_clearInterruptStatus(ADC12_0_INST,DL_ADC12_IIDX_DMA_DONE);//Clear Interrupt Status
break;
default:
break;
}
}
实际演示
因为我每次只采128个点,并且不是连续采样,所以每次重新RST,采样的信号肯定不和上次连续,就会出现下面这种尖点,从一次连续信号的角度来看,效果还不错
转载请标明出处