1.简介
简单介绍芯片内部的ADC和uDMA模块,分享ADC+uDMA+Timer触发配置。本人是边学习边记录,内容仅作参考,有错误的地方请见谅。
2.ADC介绍
本部分内容参考自TI官方手册。
芯片内部的ADC模块具有两路ADC,分别为ADC0和ADC1,共享20个输入通道,最大采样速度2M(两个ADC交替采样最大可以达到4M),可以配置成4个采样队列,支持软件触发、定时器触发、pwm触发、比较器触发、io口触发,支持硬件平均(最多64次采样),8个数字比较器,参考电压可选择。
需要注意的是4个队列的长度不一样,队列0最多支持8个通道,队列1和2支持4个通道,队列3只支持1个通道。
3.uDMA介绍
本部分内容参考自TI官方手册。
芯片内部的uDMA模块具有32个独立通道,支持内存到内存、内存到外设、外设到内存三个传输方向,支持多种传输模式,支持8位、16位、32位数据宽度,一次可传输的数据长度最大1024,源地址和目标地址自增可控。uDMA的通道配置参数存储在用户提供的RAM中,需要在程序中定义一个长度为1024字节的数组用于存放通道配置参数,这个数组需要以1024字节进行地址对齐。
4.配置代码
本部分代码实现timer触发ADC转换使用uDMA搬运,uDMA工作在ping-pong模式下。
enum BUFFER_STATUS
{
EMPTY,
FILLING,
FULL
};
//dma控制块需要1024字节对齐
static uint8_t ControlTable[1024] __attribute__ ((aligned(1024)));
uint16_t ADCBuffer1[ADC_SAMPLE_BUF_SIZE];
uint16_t ADCBuffer2[ADC_SAMPLE_BUF_SIZE];
static enum BUFFER_STATUS BufferStatus[2];
//ADC队列0中断函数
uint32_t adc_int_count = 0; //记录中断次数
void ADC0SS0_Handler(void)
{
adc_int_count++;
HWREG(ADC0_BASE + ADC_O_ISC) = HWREG(ADC0_BASE + ADC_O_RIS) & (1 << 8); //清中断
//判断哪个buffer满了,进行切换
if ((uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT) ==
UDMA_MODE_STOP) && (BufferStatus[0] == FILLING)) {
BufferStatus[0] = FULL;
BufferStatus[1] = FILLING;
} else if ((uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT) ==
UDMA_MODE_STOP) && (BufferStatus[1] == FILLING)) {
BufferStatus[0] = FILLING;
BufferStatus[1] = FULL;
}
if(BufferStatus[0] == FULL) {
BufferStatus[0] = EMPTY;
//使能另一个传输块
uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *)(ADC0_BASE + ADC_O_SSFIFO0),
ADCBuffer1, ADC_SAMPLE_BUF_SIZE);
//启动DMA通道
uDMAChannelEnable(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT);
} else if(BufferStatus[1] == FULL) {
BufferStatus[1] = EMPTY;
uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *)(ADC0_BASE + ADC_O_SSFIFO0),
ADCBuffer2, ADC_SAMPLE_BUF_SIZE);
uDMAChannelEnable(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT);
}
}
void bsp_InitAdc0(void)
{
BufferStatus[0] = FILLING;
BufferStatus[1] = EMPTY;
//使能外设时钟
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
//GPIO配置
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);
uDMAEnable();
uDMAControlBaseSet(ControlTable); //设置控制块
//清除通道属性
uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0,
UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY |
UDMA_ATTR_REQMASK);
//配置主控制块参数,16位数据宽度、源地址不自增、目标地址16bit自增、每次都触发。
uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_16 |
UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
//配置副控制块参数,16位数据宽度、源地址不自增、目标地址16bit自增、每次都触发。
uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_16 |
UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
//主控制块发送设置,ping-pong模式、源地址(void *)(ADC0_BASE + ADC_O_SSFIFO0)、
//目标地址ADCBuffer1、传输大小ADC_SAMPLE_BUF_SIZE
uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *)(ADC0_BASE + ADC_O_SSFIFO0),
ADCBuffer1, ADC_SAMPLE_BUF_SIZE);
//副控制块发送设置,ping-pong模式、源地址(void *)(ADC0_BASE + ADC_O_SSFIFO0)、
//目标地址ADCBuffer2、传输大小ADC_SAMPLE_BUF_SIZE
uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *)(ADC0_BASE + ADC_O_SSFIFO0),
ADCBuffer2, ADC_SAMPLE_BUF_SIZE);
//设置连续传输
uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0, UDMA_ATTR_USEBURST);
//开启DMA通道
uDMAChannelEnable(UDMA_CHANNEL_ADC0);
// ADC0时钟配置 全速1M PIOSC为16M时钟
ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1);
//等待时钟配置
SysCtlDelay(10);
//关闭队列0中断
IntDisable(INT_ADC0SS0);
ADCIntDisable(ADC0_BASE, 0);
//关闭队列0
ADCSequenceDisable(ADC0_BASE, 0);
ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0); //定时器触发
// ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0); //连续不断采样 全速
//队列0,编号0,通道ch0|最后一个采样通道
ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0 | ADC_CTL_END);
//使能队列0
ADCSequenceEnable(ADC0_BASE, 0);
//使能队列0的dma
ADCSequenceDMAEnable(ADC0_BASE, 0);
//开启队列0的dma传输完成中断
ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0);
//开启队列0中断控制器
IntEnable(INT_ADC0SS0);
//使能处理器中断控制器
IntMasterEnable();
//定时器配置
TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PERIODIC);
//16k频率
TimerLoadSet(TIMER0_BASE, TIMER_A, (SystemCoreClock/16000) - 1);
//使能定时器A触发
TimerControlTrigger(TIMER0_BASE, TIMER_A, true);
//使能定时器
TimerEnable(TIMER0_BASE, TIMER_A);
}
ADC0队列0转换完成后dma自动将数据放入缓存中,ping-pong模式使用了双缓冲,dma传输完成后会进入ADC0SS0_Handler中断函数,函数中切换缓存然后重新启动传输。