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

[复制链接]
 楼主| 发表于 2011-4-4 00:19 | 显示全部楼层 |阅读模式
本帖最后由 tear086 于 2011-4-4 00:37 编辑

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

main.h

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


main.c

  1. #include "main.h"
  2. /**********************************************************
  3. * 宏及变量申明
  4. **********************************************************/
  5. #define PDMA0_LEN 32 // PDMA0所传数据长度
  6. volatile uint16_t ad_x_buf[PDMA0_LEN] = {0}; // PDMA0所传数据缓存
  7. volatile uint8_t ad_offset = 0; // ADC数据寄存器偏移地址
  8. volatile uint32_t ad_x_sum = 0; // 所传PDMA0_LEN个数据之和值
  9. volatile uint16_t ad_X[2] = {0}; // 所传PDMA0_LEN个数据之均值
  10. typedef enum{NO=0, YES=!NO}bool;
  11. volatile bool tmr0_5ms_flag = YES; // 5ms标志,用作键盘显示等
  12. /**********************************************************
  13. * 系统上电初始化
  14. **********************************************************/
  15. void MAIN_INIT(void)
  16. {
  17.     UNLOCKREG();
  18.     {   /* 配置系统时钟 */
  19.         SYSCLK->PWRCON.XTL12M_EN = 1; //  设定12M外部晶振
  20.         DrvSYS_Delay(5000); // 等待时钟就绪
  21.         DrvSYS_SelectPLLSource(E_SYS_EXTERNAL_12M); // 选择12MHz为PLL输入
  22.         DrvSYS_Open(50000000); // 打开50MHz
  23.     }      
  24.     {   /* 配置串口 */
  25.         STR_UART_T param;
  26.         
  27.         DrvSYS_SelectIPClockSource(E_SYS_UART_CLKSRC, 0); //使能UART时钟
  28.         DrvGPIO_InitFunction(E_FUNC_UART0);    // 复用功能引脚设置
  29.                                                          
  30.         param.u32BaudRate        = 115200;    // 波特率
  31.         param.u8cDataBits        = DRVUART_DATABITS_8;  // 数据位
  32.         param.u8cStopBits        = DRVUART_STOPBITS_1;  // 停止位
  33.         param.u8cParity          = DRVUART_PARITY_NONE;    // 校验位
  34.         param.u8cRxTriggerLevel  = DRVUART_FIFO_1BYTES;    // FIFO存储深度1字节
  35.         param.u8TimeOut             = 0; // FIFO超时设定
  36.         DrvUART_Open(UART_PORT0, &param); // 串口开启、结构体整体赋值
  37.     }
  38.     {   /* 配置GPIO */
  39.         NVIC_DisableIRQ(GPAB_IRQn);
  40.         NVIC_DisableIRQ(GPCDE_IRQn);
  41.         DrvGPIO_Open(E_GPB, 10, E_IO_OUTPUT);
  42.         DrvGPIO_ClrBit(E_GPB, 10); // 关蜂鸣器
  43.     }
  44.     {   /* 配置TMR0 */
  45.         NVIC_DisableIRQ(TMR0_IRQn);
  46.         // 第一步 使能和选择定时器时钟源及使能定时器模块         
  47.         SYSCLK->CLKSEL1.TMR0_S = 0; // 选择12Mhz作为定时器时钟源
  48.         SYSCLK->APBCLK.TMR0_EN =1;  // 使能定时器0
  49.         TIMER0->TCSR.CEN = 1;       // 使能定时器模块
  50.         // 第二步 选择操作模式   
  51.         TIMER0->TCSR.MODE = 1; // 选择周期模式
  52.         TIMER0->TCSR.CRST = 1; // 清加1计数器   
  53.         // 第三步 输出时钟周期 = 定时器时钟源周期*(8位预分频因子 + 1) * (24位比较因子TCMP)
  54.         TIMER0->TCSR.PRESCALE = 11; // 12分频
  55.         TIMER0->TCMPR = 1;       // 1/12M * 12 * 5000 = 5ms
  56.         // 第四步 使能中断
  57.         TIMER0->TISR.TIF = 1; // 清中断  
  58.         TIMER0->TCSR.IE = 1; // 使能中断
  59.         NVIC_EnableIRQ(TMR0_IRQn);  // 使能TMR0中断
  60.         // 第五步 使能定时器模块
  61.         TIMER0->TCSR.CRST = 1; // 复位向上计数器
  62.      TIMER0->TCSR.CEN = 1; // 使能TMR0
  63.         //TIMER0->TCSR.TDR_EN=1; // 无需读取加1计数器值
  64.     }
  65.     {   /* 配置ADC */
  66.         NVIC_DisableIRQ(ADC_IRQn);
  67.         // 第一步 GPIO初始化
  68.         SYS->IPRSTC2.ADC_RST = 1; // 复位ADC
  69.      SYS->IPRSTC2.ADC_RST = 0;
  70.         GPIOA->OFFD |= 0x03 << 16; // 失能数字输入路径
  71.         SYS->GPAMFP.ADC0 = 1; // 设置ADC函数
  72.         SYS->GPAMFP.ADC1_AD12 = 1;           
  73.         // 第二步 使能及选择ADC时钟源 然后使能ADC模块
  74.         SYSCLK->CLKSEL1.ADC_S = 0;    // 选择12MHz
  75.         SYSCLK->CLKDIV.ADC_N = 0;    // 时钟源=12M/1=12MHz
  76.         SYSCLK->APBCLK.ADC_EN = 1;    // 使能时钟源
  77.         ADC->ADCR.ADEN = 1;            // 使能AD模块
  78.         // 第三步 选择操作模式
  79.         ADC->ADCR.DIFFEN = 0;  // 单端输入
  80.         ADC->ADCR.ADST = 0; // 失能ADST
  81.         ADC->ADCR.ADMD = 3; // 连续扫描         
  82.         // 第四步 选择ADC通道
  83.         ADC->ADCHER.CHEN = 0x03 ; // 使能ADC CH0、1
  84.         ADC->ADCHER.PRESEL = 0;   // 模拟输入通道选择:外部模拟输入
  85.         // 第五步 使能PDMA;第五、六两步不可同时设置
  86.         ADC->ADCR.ADIE = 0; // 失能AD中断
  87.         ADC->ADCR.PTEN = 1; // PDMA传送使能
  88.         // 第六步 使能ADC中断
  89.         //ADC->ADSR.ADF = 1;  // 清AD中断标志
  90.         //ADC->ADCR.ADIE = 1; // 使能AD中断
  91.         //NVIC_EnableIRQ(ADC_IRQn);     
  92.         // 第六步 启动ADC转换
  93.         ADC->ADCR.ADST = 1;   
  94.     }
  95.     {   /* 配置PDMA0 <- ADC0*/
  96.         // Q1:PDMA进给ADC留下一个通道选择,若是多路ADC,应当如何处理?
  97.         // A1:无他,每传输完一次PDMA,变换一次源地址
  98.         // Q2:在ISR读完一次数据后,是否要清零,等待传输新的数据?
  99.         // A2: 不需要,因为使用的传输完成中断
  100.         // Q3: 为何在使用PDMA传输ADC数据寄存器时,每次只传输了PDMA0->BCR值的一半的数次?
  101.         // A3:God knows.
  102.         NVIC_DisableIRQ(PDMA_IRQn);   
  103.         // 第一步 使能PDMA核时钟
  104.         SYSCLK->AHBCLK.PDMA_EN = 1;
  105.         // 第二步 映射PDMA通道链接
  106.         PDMA_GCR->PDSSR1.ADC_RXSEL = 0; // ADC_RX选择传至通道0
  107.         // 第三部 使能通道时钟,相当重要
  108.         PDMA_GCR->GCRCSR.CLK0_EN = 1; // 使能通道0时钟
  109.         // 第三步 设置接收和发送基地址,及相应参数
  110.         PDMA0->CSR.PDMACEN  = 1; // 使能通道0
  111.         PDMA0->CSR.SAD_SEL =  2; // 源地址设为固定模式
  112.         PDMA0->CSR.DAD_SEL =  0; // 目标地址设为自增模式
  113.         PDMA0->CSR.APB_TWS = 2; // 传输单位设为16位
  114.         PDMA0->CSR.MODE_SEL = 1; // 传输方向为外设到内存
  115.         PDMA0->SAR = ADC_BASE; // 通道0 PDMA源地址指向ADC0的数据寄存器地址;必须为32位
  116.         PDMA0->DAR = (uint32_t)ad_x_buf; // 通道0 PDMA目标地址指向ad_x_buf数组首地址;必须为32位
  117.         PDMA0->BCR = (uint16_t)(PDMA0_LEN<< 1); // 16位传输计数寄存器;注意:每传输16位计数一次
  118.      // 第四步 使能中断
  119.         PDMA0->IER.BLKD_IE = 1;  // 使能传输完成中断
  120.         PDMA0->ISR.BLKD_IF = 1; // 清传输完成中断标志
  121. //        PDMA0->IER.TABORT_IE = 1; // 使能读写异常中断
  122. //        PDMA0->ISR.TABORT_IF = 1; // 清读写异常中断标志
  123.         NVIC_EnableIRQ(PDMA_IRQn);            
  124.      // 第五步 通道及触发使能  
  125.         PDMA0->CSR.PDMACEN = 1;  // 使能PDMA核
  126.         PDMA0->CSR.TRIG_EN = 1;  // 触发使能
  127.     }
  128.     LOCKREG();
  129. }  
  130. /**********************************************************
  131. * TMR0 ISR
  132. **********************************************************/
  133. void TMR0_IRQHandler(void) __irq
  134. {
  135.     extern volatile bool tmr0_5ms_flag;
  136.     TIMER0->TISR.TIF = 1; // 清TMR0中断标志
  137.     tmr0_5ms_flag = YES; // 用作扫描按键、显示等
  138. }
  139. /**********************************************************
  140. * ADC ISR
  141. **********************************************************/
  142. // ADC0~共用一个ISR
  143. //void ADC_IRQHandler(void) __irq
  144. //{
  145. //    ADC->ADSR.ADF = 1;  // 清AD中断标志
  146. //    ADC->ADCR.ADST = 0;  // 停止ADC转换
  147. //}
  148. /**********************************************************
  149. * PDMA ISR
  150. **********************************************************/
  151. // 通道0~n共用一个ISR
  152. void PDMA_IRQHandler(void) __irq
  153. {  
  154.     PDMA0->ISR.BLKD_IF = 1; // 清中断标志
  155.     /* 检查PDMA0是否传输完毕指定数次的数据 */
  156.     // 此处的CBCR可看作一个-1计数器,当记满时为0,其区间为[0, CCB)
  157.     if(0 == PDMA0->CBCR.CBCR) {
  158.         if(0x00 == ad_offset) // 传输ADC0
  159.         {
  160.             int i=0;        
  161.             do{
  162.                 ad_x_sum += ad_x_buf[i];
  163.             }while(++i < PDMA0_LEN);
  164.             ad_X[0] = ad_x_sum >> 5 /* / PDMA0_LEN*/;
  165.             ad_x_sum = 0;
  166.             
  167.             ad_offset = 0x04;
  168.         }
  169.         else if(0x04 == ad_offset) // 传输ADC1
  170.         {
  171.             int i=0;
  172.             do{
  173.                 ad_x_sum += ad_x_buf[i];
  174.             }while(++i < PDMA0_LEN);
  175.             ad_X[1] = ad_x_sum >> 5 /* / PDMA0_LEN*/;
  176.             ad_x_sum = 0;
  177.             
  178.             ad_offset = 0x00;
  179.         }
  180.         
  181.         PDMA0->SAR = ADC_BASE + ad_offset; // 切换PDMA源地址
  182.     }

  183.     PDMA0->CSR.TRIG_EN = 1; // 使能触发;注意:当PDMA完成传输后,该位自动清零
  184. }
  185. /**********************************************************
  186. * 主函数
  187. **********************************************************/
  188. int main(void)
  189. {
  190.     extern volatile uint16_t ad_X[2];
  191.     extern volatile bool tmr0_5ms_flag;
  192.     MAIN_INIT(); // 上电初始化系统     
  193.    
  194.     while(1) {
  195.         /* 扫描按键、显示等 */
  196.         if(YES == tmr0_5ms_flag) {
  197.             tmr0_5ms_flag = NO;
  198.             PRINT("V0 = %d.%d V, V1 = %d.%d V\r",
  199.                 (ad_X[0]*5000>>12)/1000, (ad_X[0]*5000>>12)%1000,
  200.                 (ad_X[1]*5000>>12)/1000, (ad_X[1]*5000>>12)%1000);            
  201.         }
  202.         /* 跳出大循环 */
  203.         if(0) break;
  204.     }
  205.    
  206.     DrvUART_Close(UART_PORT0);
  207.     return 0;
  208. }

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

评分

参与人数 2威望 +11 收起 理由
dong_abc + 1
hotpower + 10

查看全部评分

发表于 2013-7-15 17:30 | 显示全部楼层
多谢楼主分享!
发表于 2011-7-13 10:20 | 显示全部楼层
谢谢楼主的奉献!
发表于 2011-5-23 17:55 | 显示全部楼层
收下!!
发表于 2011-4-13 13:58 | 显示全部楼层
顶起来,俺认为楼主应该把新唐的pdma的所有应用做成系列笔记,若结合fifo就更好了.
要突出芯片应用的特点和特色,
发表于 2011-4-9 16:32 | 显示全部楼层
好贴,每一步都很详细!
发表于 2011-4-9 12:46 | 显示全部楼层
把pdma继续
 楼主| 发表于 2011-4-8 12:56 | 显示全部楼层
6# hotpower
不敢当。
发表于 2011-4-8 08:20 | 显示全部楼层
哪天楼主也群课?
发表于 2011-4-7 20:58 | 显示全部楼层
学习!!
发表于 2011-4-4 17:25 | 显示全部楼层
很给力!!!
发表于 2011-4-4 10:57 | 显示全部楼层
好资料!
发表于 2011-4-4 08:01 | 显示全部楼层
顶!沙发!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:個人博客:yuphone.cnblogs.com 聯絡方式:張亞峰 15013013265@qq.com

0

主题

120

帖子

3

粉丝
快速回复 返回顶部 返回列表