• [原创].菜农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

    #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

    #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;
    }
    
    Snap6 Snap7
  • 相关阅读:
    软件工程作业--评价自己经常使用的输入法
    课堂练习-找水王
    软件工程——找水王(续)
    软件工程——评价输入法
    软件工程——《你的灯亮着吗》读书笔记
    软件工程——课堂练习“找水王”
    软件工程结队项目——智能点餐系统典型用户及用户场景分析
    软件工程课堂练习——N层电梯只停一层求乘客爬楼层数最少(基本方法+优化方法)
    软件工程课堂练习——求买书最低价格
    结队项目——智能订餐系统用户调研报告
  • 原文地址:https://www.cnblogs.com/yuphone/p/2004956.html
Copyright © 2020-2023  润新知