二十二、ADC+MDA传输
1. 配置步骤
我们在本次实验中使用的是PA5引脚的ADC1通道5来采集数据,详细的步骤:
ADC初始化:
- 调用
ADC_init()
函数进行ADC的初始化。 - 在
ADC_init()
函数中:- 关闭相关的寄存器写保护。
- 使能ADC模块的时钟。
- 配置ADC引脚为模拟输入模式。
- 初始化ADC结构体并设置相关参数,如扫描模式、分辨率和数据对齐方式。
- 初始化ADC模块,将初始化后的结构体传入
ADC_Init
函数。 - 使能ADC通道,此处是启用了ADC序列A的通道5。
- 调用
DMA初始化:
- 调用
ADC_DMA_Init()
函数进行DMA的初始化。 - 在
ADC_DMA_Init()
函数中:- 使能DMA和AOS模块的时钟。
- 设置AOS的触发事件源为ADC1的转换结束中断。
- 初始化DMA结构体并设置相关参数,包括中断使能、数据传输块大小、传输计数、数据宽度、源地址和目标地址等。
- 初始化DMA,将初始化后的结构体传入
DMA_Init
函数。 - 设置DMA的重复传输模式,将初始化后的结构体传入
DMA_RepeatInit
函数。 - 启动ADC转换,使用
ADC_Start
函数启动ADC转换。 - 启用DMA通道,使用
DMA_Cmd
函数启动DMA传输,同时使用DMA_ChCmd
函数启用DMA通道。
- 调用
数据采集和处理:
- 在
main
函数的主循环中,通过串口打印ADC采样值,以检查数据是否正确。 - 使用延时函数延时一定时间后再次进行数据采集。
- 在
2. ADC初始化
代码如下:
c
void ADC_init(void)
{
// 关闭相关的寄存器写保护
LL_PERIPH_WE(LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU);
// 使能ADC时钟
FCG_Fcg3PeriphClockCmd(FCG3_PERIPH_ADC1, ENABLE);
stc_gpio_init_t stcGpioInit;
// ADC引脚初始化
(void)GPIO_StructInit(&stcGpioInit);
stcGpioInit.u16PinAttr = PIN_ATTR_ANALOG; // 设置引脚属性为模拟输入
(void)GPIO_Init(GPIO_PORT_A, GPIO_PIN_05, &stcGpioInit); // 初始化GPIO
stc_adc_init_t stcAdcInit;
// 使用基本参数初始化ADC结构体
(void)ADC_StructInit(&stcAdcInit);
stcAdcInit.u16ScanMode = ADC_MD_SEQA_CONT; // 序列A连续,序列B禁用
stcAdcInit.u16Resolution = ADC_RESOLUTION_12BIT; // 设置ADC分辨率为12位
stcAdcInit.u16DataAlign = ADC_DATAALIGN_RIGHT; // 设置ADC数据右对齐
// 初始化ADC
(void)ADC_Init(CM_ADC1, &stcAdcInit);
// ADC使能
ADC_ChCmd(CM_ADC1, ADC_SEQ_A, ADC_CH5, ENABLE); // 使能ADC通道5
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- 关闭相关的寄存器写保护:
- 使用
LL_PERIPH_WE(LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU)
关闭相关寄存器的写保护,以便能够配置相关的寄存器。
- 使能ADC时钟:
- 使用
FCG_Fcg3PeriphClockCmd(FCG3_PERIPH_ADC1, ENABLE)
使能ADC1模块的时钟,确保ADC1模块能够正常工作。
- ADC引脚初始化:
- 定义了一个
stc_gpio_init_t
类型的结构体变量stcGpioInit
,用于配置GPIO初始化参数。 - 使用
GPIO_StructInit(&stcGpioInit)
将GPIO初始化结构体初始化为默认值,以确保所有参数都被正确配置。 - 设置了
u16PinAttr
参数为PIN_ATTR_ANALOG
,将引脚属性设置为模拟输入,以便连接到ADC通道。 - 调用
GPIO_Init(GPIO_PORT_A, GPIO_PIN_05, &stcGpioInit)
初始化GPIO,将引脚 PA5 设置为模拟输入模式。
- 初始化ADC结构体:
- 定义了一个
stc_adc_init_t
类型的结构体变量stcAdcInit
,用于配置ADC初始化参数。 - 使用
ADC_StructInit(&stcAdcInit)
将ADC初始化结构体初始化为默认值,以确保所有参数都被正确配置。 - 设置了
u16ScanMode
参数为ADC_MD_SEQA_CONT
,表示选择序列A连续模式,即连续采样模式,用于持续地对多个通道进行采样。 - 设置了
u16Resolution
参数为ADC_RESOLUTION_12BIT
,表示ADC的分辨率为12位,即每个采样值的精度为12位。 - 设置了
u16DataAlign
参数为ADC_DATAALIGN_RIGHT
,表示ADC采样数据右对齐,即数据的高位对齐。
- 初始化ADC:
- 使用
ADC_Init(CM_ADC1, &stcAdcInit)
对ADC1模块进行初始化,将之前配置的ADC初始化参数应用到ADC1模块中。
- ADC使能:
- 使用
ADC_ChCmd(CM_ADC1, ADC_SEQ_A, ADC_CH5, ENABLE)
使能ADC1模块的通道5,即使能ADC通道5进行采样。
3. DMA初始化
代码如下:
c
void ADC_DMA_Init(void)
{
// 使能DMA时钟和AOS时钟
FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_DMA1, ENABLE);
FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_AOS, ENABLE);
// 设置DMA触发源为ADC1转换完成事件
AOS_SetTriggerEventSrc(AOS_DMA1_0, EVT_SRC_ADC1_EOCA);
stc_dma_init_t stcDmaInit;
stc_dma_repeat_init_t stcDmaRptInit;
// 初始化DMA结构体
(void)DMA_StructInit(&stcDmaInit);
stcDmaInit.u32IntEn = DMA_INT_DISABLE; // 禁止DMA中断
stcDmaInit.u32BlockSize = 1; // 设置DMA块大小为1
stcDmaInit.u32TransCount = 0; // 设置DMA传输计数为0
stcDmaInit.u32DataWidth = DMA_DATAWIDTH_16BIT; // 设置DMA数据宽度为16位
stcDmaInit.u32DestAddr = (uint32_t)(&m_au16AdcSaVal[]); // 设置DMA目标地址为ADC采样值数组的地址
stcDmaInit.u32SrcAddr = (uint32_t)(&CM_ADC1->DR5); // 设置DMA源地址为ADC1数据寄存器DR5的地址
stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_FIX; // 设置DMA源地址增量模式为固定地址模式
stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_FIX; // 设置DMA目标地址增量模式为固定地址模式
// 初始化DMA
(void)DMA_Init(CM_DMA1, DMA_CH0, &stcDmaInit);
stcDmaRptInit.u32Mode = DMA_RPT_BOTH; // 设置DMA重复传输模式为双向
stcDmaRptInit.u32SrcCount = 1; // 设置DMA源计数为1
stcDmaRptInit.u32DestCount = 1; // 设置DMA目标计数为1
(void)DMA_RepeatInit(CM_DMA1, DMA_CH0, &stcDmaRptInit);
DMA_Cmd(CM_DMA1, ENABLE); // 使能DMA
DMA_ChCmd(CM_DMA1, DMA_CH0, ENABLE); // 使能DMA通道0
ADC_Start(CM_ADC1); // 启动ADC1转换
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- 使能DMA时钟和AOS时钟:
- 使用
FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_DMA1, ENABLE)
和FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_AOS, ENABLE)
使能DMA和AOS模块的时钟,确保它们可以正常工作。
- 设置DMA触发源为ADC1转换完成事件:
- 使用
AOS_SetTriggerEventSrc(AOS_DMA1_0, EVT_SRC_ADC1_EOCA)
将DMA的触发源设置为ADC1的转换完成事件,这样当ADC1完成一次转换时,就会触发DMA传输。
- 初始化DMA结构体:
- 首先,定义了两个DMA相关的结构体变量
stc_dma_init_t stcDmaInit
和stc_dma_repeat_init_t stcDmaRptInit
,用于配置DMA的初始化参数和重复传输参数。 - 使用
DMA_StructInit(&stcDmaInit)
将DMA的初始化结构体初始化为默认值,以确保所有参数都被正确配置。
- 配置DMA初始化参数:
u32IntEn
:禁止DMA中断,设置为DMA_INT_DISABLE
。u32BlockSize
:设置DMA块大小为1,表示每次DMA传输的数据块大小为1个单位。u32TransCount
:设置DMA传输计数为0,表示DMA传输次数为0,意味着DMA传输次数不受限制。u32DataWidth
:设置DMA数据宽度为16位,表示DMA传输的数据位宽为16位。u32DestAddr
:设置DMA目标地址为ADC采样值数组的地址,即将ADC转换结果存储到数组中。u32SrcAddr
:设置DMA源地址为ADC1数据寄存器DR5的地址,即从ADC1的DR5寄存器中读取转换结果。u32SrcAddrInc
:设置DMA源地址增量模式为固定地址模式,表示DMA传输过程中源地址不会自动增加。u32DestAddrInc
:设置DMA目标地址增量模式为固定地址模式,表示DMA传输过程中目标地址不会自动增加。
- 初始化DMA:
- 使用
DMA_Init(CM_DMA1, DMA_CH0, &stcDmaInit)
初始化DMA,将之前配置的DMA初始化参数应用到DMA模块中。
- 配置DMA重复传输参数:
u32Mode
:设置DMA重复传输模式为双向,表示DMA的传输模式为双向重复传输。u32SrcCount
:设置DMA源计数为1,表示每次DMA传输完成后,源地址保持不变。u32DestCount
:设置DMA目标计数为1,表示每次DMA传输完成后,目标地址保持不变。- 使用
DMA_RepeatInit(CM_DMA1, DMA_CH0, &stcDmaRptInit)
对DMA重复传输进行初始化,将之前配置的DMA重复传输参数应用到DMA模块中。
- 使能DMA和DMA通道:
- 使用
DMA_Cmd(CM_DMA1, ENABLE)
使能DMA模块。 - 使用
DMA_ChCmd(CM_DMA1, DMA_CH0, ENABLE)
使能DMA通道0。
- 启动ADC1转换:
- 使用
ADC_Start(CM_ADC1)
启动ADC1的转换,这样当ADC1完成一次转换时,就会触发DMA传输。
4. 数据采集和处理
我们完成初始化之后,想要使用数据,就可以直接访问数组将数据取出即可。
c
static uint16_t m_au16AdcSaVal[]; // 存储ADC采样值的数组,长度为1
int32_t main(void)
{
board_init(); // 初始化板载硬件
uart1_init(115200U); // 初始化UART1串口通信,波特率为115200
// 关闭寄存器写保护
LL_PERIPH_WE(LL_PERIPH_ALL);
ADC_init(); // 初始化ADC
ADC_DMA_Init(); // 初始化ADC DMA
while(1)
{
// 打印ADC采样值
printf("value = %d\r\n", m_au16AdcSaVal[]);
delay_ms(1000); // 延时1秒
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
5. 实验现象
烧写我们的代码之后,在串口调试助手就可以查看PA5引脚采集的数据。