MSPM0L1306例程学习系列
使用的TI的官方例程,即SDK里边包含的例程代码。
可以到TI官网下载并且安装SDK: https://www.ti.com.cn/tool/cn/download/MSPM0-SDK/
MCU使用的是MSPM0L1306, 对于ADC部分,有10个例程:
前边讲了4个例程,今天接着讲2个例程,通过DMA进行进行ADC转换结果的传输,其中一个例程是12位的分辨率,另一个例程是8位的分辨率。 两个例子的简单比较如下:
- 在AD的工作模式都选择了单通道 多次转换的工作模式,选择adc通道2(PA25),这个可以根据实际需要进行修改的;
- 参考电压的选择上,都直接使用了电源电压作为基准电压。 要特别注意,如果使用内部参考电压源,那么ADC的时钟上限为4MHz。
- 有个采样保持时间,通过定时计数器0或者1来设置。这个跟ADC的分辨率有关,具体可以查看手册,里边有个公式计算。12位分辨率,例程里是250ns;8位分辨率,例程设置的是62.5ns。
- 启用了FIFO寄存器,是一个32位的寄存器,将从这个寄存器读取ADC的转换结果。因为是32位的,所以会将两次的转换结果存放在一起。所以,如果转换了1000次,实际上只要读500次,因为每次能读取两个结果。目前是这么理解的,有错的话,后续再回来更正:)
- 需要重点理解的是,DMA触发的相关配置。
a. DMA Samples Count,一次DMA触发要传输的ADC转换结果数;
b. Enable DMA Triggers, 配置可以触发DMA进行传输的触发源。
第1个例程的注解如下:
下边这一页的配置要重点理解下,总感觉例程有不是很合理的地方~
代码简单注释如下:
c
/*
* ADC转换的SDK例程
* 文件名:adc12_max_freq_dma.c
* 描述:
* ADC转换使用DMA来传输结果,12位分辨率。
* 1、单通道、多次转换、自动采样模式、软件触发;
* 2、启用FIFO,选择ADC输入通道2(PA25)
* 3、直接使用电源电压做参考电压
* 4、转换ADC_SAMPLE_SIZE=1024次,将通过DMA存放到数组gADCSamples[]中;
* 5、代码中设有断点语句__BKPT(0),会自动进入断点,查看数据;
*
* 操作描述:
* 1、下载程序;
* 2、添加观察变量gADCSamples,全速运行;
* 3、系统会自动停在断点处,查看ADC的采样结果值;
*
* 注意事项:
* 1、注意系统时钟的配置,ADC的时钟为32MHz
* 2、在SYSCONFIG图形配置工具中并没有完成所有的参数配置;
* 部分的参数配置,在主程序通过调用库函数的形式进行重新配置;
* 3、不能使用内部参考电压源VREF,启用内部电压,ADC时钟限制为4MHz;
*
* 思考:
* 1、程序只跑一遍,如果想要不断的执行,while(1)循环应该怎么修改?
* 2、如果没有外部电压接入PA25, 把ADC的通道换成A15,内部源监控通道,VDD/3,数据比较规律?
*
* 修改:
* 基于官方的sdk例程增加注释,xie_sx@126.com
*/
#include "ti_msp_dl_config.h"
//宏定义了ADC的转换次数
#define ADC_SAMPLE_SIZE (1024)
//当启用FIFO时,2个转换结果会被压缩为一个32位存放在FIFODATA寄存器
//所以,读取FIFO的次数要减半,右移一位
#define ADC_FIFO_SAMPLES (ADC_SAMPLE_SIZE >> 1)
//定义16位的数组来存放ADC的转换结果.
uint16_t gADCSamples[ADC_SAMPLE_SIZE];
//标志变量,ADC的转换结果是否已经传输完成;
volatile bool gCheckADC;
int main(void)
{
//器件初始化
SYSCFG_DL_init();
//DMA的基本工作模式在SYSCONFIG里已经配置;
//此处配置DMA的传输通道信息,一般要配置3个参数,从哪搬到哪,搬多少
//1、源地址, FIFODATA寄存器的地址,从这个寄存器读取数据往外搬
//2、目的地址, 存档转换结果的数组的起始地址, 直接将数据搬到数组中
//3、传输大小,要搬运的次数
DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID,(uint32_t) DL_ADC12_getFIFOAddress(ADC12_0_INST));
DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gADCSamples[0]);
DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_SAMPLES);
DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
//配置器件的中断
NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);
//ADC12的转换完成标志位清零
gCheckADC = false;
//软件启动ADC12转换
DL_ADC12_startConversion(ADC12_0_INST);
//等待ADC12的转换完成标志位变为true,该标志在中断里设置
while (false == gCheckADC)
{
__WFE();
}
//断点,程序运行到此处会自动进入断点,可查看ADC的转换结果
__BKPT(0);
while (1)
{
__WFI();
}
}
/*
* ADC12中断处理函数
* 通过DMA传输完成中断来判断ADC转换结束
*
*/
void ADC12_0_INST_IRQHandler(void)
{
switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST))
{
case DL_ADC12_IIDX_DMA_DONE:
gCheckADC = true;
break;
default:
break;
}
}
第2个例程的注解如下:
代码简单注释如下:
c
/*
* ADC转换的SDK例程
* 文件名:adc12_max_freq_dma_8bit.c
* 描述:
* ADC转换使用DMA来传输结果,8位分辨率
* 1、单通道、多次转换、自动采样模式、软件触发;
* 2、启用FIFO,选择ADC输入通道2(PA25)
* 3、直接使用电源电压做参考电压
* 4、转换ADC_SAMPLE_SIZE=1024次,将通过DMA存放到数组gADCSamples[]中;
* 5、代码中设有断点语句__BKPT(0),会自动进入断点,查看数据;
*
* 操作描述:
* 1、下载程序;
* 2、添加观察变量gADCSamples,全速运行;
* 3、系统会自动停在断点处,查看ADC的采样结果值;
*
* 注意事项:
* 1、注意ADC的时钟选择SYSOSC,工作在32MHz
* 2、在SYSCONFIG图形配置工具中并没有完成所有的参数配置;
* 部分的参数配置,在主程序通过调用库函数的形式进行重新配置;
* 3、不能使用内部参考电压源VREF,启用内部电压,ADC时钟限制为4MHz;
*
* 思考:
* 1、代码有一定的优化空间.例如,两个中断里的标志没有具体区分开来
*
* 修改:
* 基于官方的sdk例程增加注释,xie_sx@126.com
*/
#include "ti_msp_dl_config.h"
#define ADC_SAMPLE_SIZE (1024)
//当启用FIFO时,2个转换结果会被压缩为一个32位的数据存放在FIFODATA寄存器
#define ADC_FIFO_SAMPLES (ADC_SAMPLE_SIZE >> 1)
uint16_t gADCSamples[ADC_SAMPLE_SIZE];
volatile bool gCheckADC;
volatile bool gADCError;
int main(void)
{
//器件初始化
SYSCFG_DL_init();
//DMA的基本工作模式在SYSCONFIG里已经配置;
//此处配置DMA的传输通道信息,一般要配置3个参数,从哪搬到哪,搬多少
//1、源地址, FIFODATA寄存器的地址,从这个寄存器读取数据往外搬
//2、目的地址, 存档转换结果的数组的起始地址, 直接将数据搬到数组中
//3、传输大小,要搬运的次数。 放到了while(1)循环中
DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID,(uint32_t) DL_ADC12_getFIFOAddress(ADC12_0_INST));
DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gADCSamples[0]);
//配置器件的中断
NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);
gCheckADC = false;
gADCError = false;
while (1)
{
DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_SAMPLES);
DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
DL_ADC12_enableDMA(ADC12_0_INST);
DL_ADC12_startConversion(ADC12_0_INST);
//查询ADC是否处于忙状态。
//在单通道多次转换的模式下,一旦ADC被触发,无论转换是否停止,ADC将继续采样,直到完成相应次数的转换
//??需要进一步探讨下,这个语句的具体作用
if (DL_ADC12_STATUS_CONVERSION_ACTIVE == DL_ADC12_getStatus(ADC12_0_INST))
{
DL_ADC12_stopConversion(ADC12_0_INST);
}
while ((false == gCheckADC) && (false == gADCError))
{
//只有两个条件都满足,才在这里等待
__WFE();
}
//为什么要先禁用,有启用? 感觉没必要??
DL_ADC12_disableConversions(ADC12_0_INST);
DL_ADC12_enableConversions(ADC12_0_INST);
//断点,程序运行到此处会自动进入断点,可查看ADC的转换结果
__BKPT(0);
gCheckADC = false;
gADCError = false;
}
}
/*
* ADC12中断处理函数
*
*/
void ADC12_0_INST_IRQHandler(void)
{
switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST))
{
case DL_ADC12_IIDX_DMA_DONE:
gCheckADC = true;
break;
case DL_ADC12_IIDX_UNDERFLOW:
gADCError = true;
break;
default:
break;
}
}