打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
菜农M0助学板PDMA读取ADC样本小练(寄存器操作方式)

[原创].菜农M0助学板PDMA读取ADC样本小练(寄存器操作方式)

已发帖至:http://bbs.21ic.com/icview-228135-1-1.html

关于菜农M0助学板(NUC120):http://bbs.21ic.com/iclist-78.html

代码大意:使用PDMA0一次读取PDMA0_LEN 个ADC样本,然后在PDMA中断中求其均值。因为PDMA0的PDSSR1寄存器只可同时映射到一路ADC,因此在PDMA0中断中切换PDMA0源地址,此法较为拙劣,仅作演示用途。

main.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef __MAIN_H__
#define __MAIN_H__
  
#include <stdio.h>
  
/*********************************************************
* 系统寄存器映射及库头文件
*********************************************************/
#include "NUC1xx.h" // 系统寄存器映射
#include "DrvSYS.h"
#include "DrvGPIO.h"
#include "DrvUART.h"
/**********************************************************
* 自定义宏
**********************************************************/
#define APP_DEBUG
  
#ifdef APP_DEBUG
    #define PRINT printf
#else
    #define PRINT 
#endif
  
#endif /* __MAIN_H__ */

main.c

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#include "main.h"
  
/**********************************************************
* 宏及变量申明
**********************************************************/
#define PDMA0_LEN 32 // PDMA0所传数据长度
volatile uint16_t ad_x_buf[PDMA0_LEN] = {0}; // PDMA0所传数据缓存
volatile uint8_t ad_offset = 0; // ADC数据寄存器偏移地址
volatile uint32_t ad_x_sum = 0; // 所传PDMA0_LEN个数据之和值
volatile uint16_t ad_X[2] = {0}; // 所传PDMA0_LEN个数据之均值 
  
typedef enum{NO=0, YES=!NO}bool;
volatile bool tmr0_5ms_flag = YES; // 5ms标志,用作键盘显示等
  
/**********************************************************
* 系统上电初始化
**********************************************************/
void MAIN_INIT(void)
{
    UNLOCKREG();
    {   /* 配置系统时钟 */
        SYSCLK->PWRCON.XTL12M_EN = 1; //  设定12M外部晶振
        DrvSYS_Delay(5000); // 等待时钟就绪
        DrvSYS_SelectPLLSource(E_SYS_EXTERNAL_12M); // 选择12MHz为PLL输入
        DrvSYS_Open(50000000); // 打开50MHz
    }      
    {   /* 配置串口 */
        STR_UART_T param;
          
        DrvSYS_SelectIPClockSource(E_SYS_UART_CLKSRC, 0); //使能UART时钟
        DrvGPIO_InitFunction(E_FUNC_UART0);    // 复用功能引脚设置
                                                           
        param.u32BaudRate        = 115200;    // 波特率
        param.u8cDataBits        = DRVUART_DATABITS_8;  // 数据位
        param.u8cStopBits        = DRVUART_STOPBITS_1;  // 停止位
        param.u8cParity          = DRVUART_PARITY_NONE;    // 校验位
        param.u8cRxTriggerLevel  = DRVUART_FIFO_1BYTES;    // FIFO存储深度1字节
        param.u8TimeOut             = 0; // FIFO超时设定
        DrvUART_Open(UART_PORT0, &param); // 串口开启、结构体整体赋值
    }
    {   /* 配置GPIO */
        NVIC_DisableIRQ(GPAB_IRQn);
        NVIC_DisableIRQ(GPCDE_IRQn);
        DrvGPIO_Open(E_GPB, 10, E_IO_OUTPUT);
        DrvGPIO_ClrBit(E_GPB, 10); // 关蜂鸣器
    }
    {   /* 配置TMR0 */
        NVIC_DisableIRQ(TMR0_IRQn);
        // 第一步 使能和选择定时器时钟源及使能定时器模块          
        SYSCLK->CLKSEL1.TMR0_S = 0; // 选择12Mhz作为定时器时钟源 
        SYSCLK->APBCLK.TMR0_EN =1;  // 使能定时器0
        TIMER0->TCSR.CEN = 1;       // 使能定时器模块
        // 第二步 选择操作模式    
        TIMER0->TCSR.MODE = 1; // 选择周期模式
        TIMER0->TCSR.CRST = 1; // 清加1计数器    
        // 第三步 输出时钟周期 = 定时器时钟源周期*(8位预分频因子 + 1) * (24位比较因子TCMP)
        TIMER0->TCSR.PRESCALE = 11; // 12分频
        TIMER0->TCMPR = 1;       // 1/12M * 12 * 5000 = 5ms 
        // 第四步 使能中断
        TIMER0->TISR.TIF = 1; // 清中断  
        TIMER0->TCSR.IE = 1; // 使能中断 
        NVIC_EnableIRQ(TMR0_IRQn);  // 使能TMR0中断
        // 第五步 使能定时器模块
        TIMER0->TCSR.CRST = 1; // 复位向上计数器
        TIMER0->TCSR.CEN = 1; // 使能TMR0
        //TIMER0->TCSR.TDR_EN=1; // 无需读取加1计数器值
    }
    {   /* 配置ADC */
        NVIC_DisableIRQ(ADC_IRQn);
        // 第一步 GPIO初始化
        SYS->IPRSTC2.ADC_RST = 1; // 复位ADC
        SYS->IPRSTC2.ADC_RST = 0; 
        GPIOA->OFFD |= 0x03 << 16; // 失能数字输入路径
        SYS->GPAMFP.ADC0 = 1; // 设置ADC函数 
        SYS->GPAMFP.ADC1_AD12 = 1;           
        // 第二步 使能及选择ADC时钟源 然后使能ADC模块
        SYSCLK->CLKSEL1.ADC_S = 0;    // 选择12MHz
        SYSCLK->CLKDIV.ADC_N = 0;    // 时钟源=12M/1=12MHz
        SYSCLK->APBCLK.ADC_EN = 1;    // 使能时钟源
        ADC->ADCR.ADEN = 1;            // 使能AD模块 
        // 第三步 选择操作模式
        ADC->ADCR.DIFFEN = 0;  // 单端输入
        ADC->ADCR.ADST = 0; // 失能ADST
        ADC->ADCR.ADMD = 3; // 连续扫描         
        // 第四步 选择ADC通道
        ADC->ADCHER.CHEN = 0x03 ; // 使能ADC CH0、1
        ADC->ADCHER.PRESEL = 0;   // 模拟输入通道选择:外部模拟输入 
        // 第五步 使能PDMA;第五、六两步不可同时设置 
        ADC->ADCR.ADIE = 0; // 失能AD中断 
        ADC->ADCR.PTEN = 1; // PDMA传送使能
        // 第六步 使能ADC中断
        //ADC->ADSR.ADF = 1;  // 清AD中断标志 
        //ADC->ADCR.ADIE = 1; // 使能AD中断
        //NVIC_EnableIRQ(ADC_IRQn);     
        // 第六步 启动ADC转换
        ADC->ADCR.ADST = 1;    
    }
    {   /* 配置PDMA0 <- ADC0*/ 
        // Q1:PDMA进给ADC留下一个通道选择,若是多路ADC,应当如何处理?
        // A1:无他,每传输完一次PDMA,变换一次源地址
        // Q2:在ISR读完一次数据后,是否要清零,等待传输新的数据?
        // A2: 不需要,因为使用的传输完成中断
        // Q3: 为何在使用PDMA传输ADC数据寄存器时,每次只传输了PDMA0->BCR值的一半的数次?
        // A3:God knows.
        NVIC_DisableIRQ(PDMA_IRQn);    
        // 第一步 使能PDMA核时钟
        SYSCLK->AHBCLK.PDMA_EN = 1;
        // 第二步 映射PDMA通道链接
        PDMA_GCR->PDSSR1.ADC_RXSEL = 0; // ADC_RX选择传至通道0
        // 第三部 使能通道时钟,相当重要
        PDMA_GCR->GCRCSR.CLK0_EN = 1; // 使能通道0时钟
        // 第三步 设置接收和发送基地址,及相应参数
        PDMA0->CSR.PDMACEN     = 1; // 使能通道0
        PDMA0->CSR.SAD_SEL =  2; // 源地址设为固定模式
        PDMA0->CSR.DAD_SEL =  0; // 目标地址设为自增模式
        PDMA0->CSR.APB_TWS = 2; // 传输单位设为16位 
        PDMA0->CSR.MODE_SEL = 1; // 传输方向为外设到内存 
        PDMA0->SAR = ADC_BASE; // 通道0 PDMA源地址指向ADC0的数据寄存器地址;必须为32位
        PDMA0->DAR = (uint32_t)ad_x_buf; // 通道0 PDMA目标地址指向ad_x_buf数组首地址;必须为32位
        PDMA0->BCR = (uint16_t)(PDMA0_LEN<< 1); // 16位传输计数寄存器;注意:每传输16位计数一次 
        // 第四步 使能中断
        PDMA0->IER.BLKD_IE = 1;  // 使能传输完成中断
        PDMA0->ISR.BLKD_IF = 1; // 清传输完成中断标志
//        PDMA0->IER.TABORT_IE = 1; // 使能读写异常中断
//        PDMA0->ISR.TABORT_IF = 1; // 清读写异常中断标志
        NVIC_EnableIRQ(PDMA_IRQn);            
        // 第五步 通道及触发使能        
        PDMA0->CSR.PDMACEN = 1;  // 使能PDMA核
        PDMA0->CSR.TRIG_EN = 1;  // 触发使能
    }
    LOCKREG();
}  
  
/**********************************************************
* TMR0 ISR
**********************************************************/
void TMR0_IRQHandler(void) __irq
{
    extern volatile bool tmr0_5ms_flag;
  
    TIMER0->TISR.TIF = 1; // 清TMR0中断标志
  
    tmr0_5ms_flag = YES; // 用作扫描按键、显示等 
}
  
/**********************************************************
* ADC ISR
**********************************************************/
// ADC0~共用一个ISR
//void ADC_IRQHandler(void) __irq
//{
//    ADC->ADSR.ADF = 1;  // 清AD中断标志 
//    ADC->ADCR.ADST = 0;  // 停止ADC转换
//}
  
/**********************************************************
* PDMA ISR
**********************************************************/
// 通道0~n共用一个ISR
void PDMA_IRQHandler(void) __irq
{  
    PDMA0->ISR.BLKD_IF = 1; // 清中断标志
  
    /* 检查PDMA0是否传输完毕指定数次的数据 */
    // 此处的CBCR可看作一个-1计数器,当记满时为0,其区间为[0, CCB)
    if(0 == PDMA0->CBCR.CBCR) {
        if(0x00 == ad_offset) // 传输ADC0
        {
            int i=0;        
            do{
                ad_x_sum += ad_x_buf[i];
            }while(++i < PDMA0_LEN);
            ad_X[0] = ad_x_sum >> 5 /* / PDMA0_LEN*/;
            ad_x_sum = 0;
              
            ad_offset = 0x04;
        }
        else if(0x04 == ad_offset) // 传输ADC1
        {
            int i=0;
            do{
                ad_x_sum += ad_x_buf[i];
            }while(++i < PDMA0_LEN);
            ad_X[1] = ad_x_sum >> 5 /* / PDMA0_LEN*/;
            ad_x_sum = 0;
              
            ad_offset = 0x00;
        }
          
        PDMA0->SAR = ADC_BASE + ad_offset; // 切换PDMA源地址
    }
  
  
    PDMA0->CSR.TRIG_EN = 1; // 使能触发;注意:当PDMA完成传输后,该位自动清零
  
/**********************************************************
* 主函数
**********************************************************/
int main(void)
{
    extern volatile uint16_t ad_X[2];
    extern volatile bool tmr0_5ms_flag;
  
    MAIN_INIT(); // 上电初始化系统     
      
    while(1) {
        /* 扫描按键、显示等 */
        if(YES == tmr0_5ms_flag) {
            tmr0_5ms_flag = NO;
  
            PRINT("V0 = %d.%d V, V1 = %d.%d V\r"
                (ad_X[0]*5000>>12)/1000, (ad_X[0]*5000>>12)%1000,
                (ad_X[1]*5000>>12)/1000, (ad_X[1]*5000>>12)%1000);             
        }
  
        /* 跳出大循环 */
        if(0) break;
    }
      
    DrvUART_Close(UART_PORT0);
    return 0;
}
 .COM 缺氧? / CC BY 2.5     
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
过采样技术
如何在PIC单片机的GPIO引脚上生成PWM信号
PIC单片机的学习心得
PIC16F877的Timer1
STM8的ADC的五种工作模式
STM8S各个模块初始化
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服