已发帖至: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, ¶m); // 串口开启、结构体整体赋值 } { /* 配置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; } |
联系客服