前一个例子,是使用轮询模式执行ADC采集处理,本例使用中断模式,占用的是5号中断向量。代码如下:
//测试工作频率为 11.0592MHz // 测试使用Timer2作为串口1的波特率发生器 // 串口1:P3.6(RXD),P3.7(TXD) #include "Ai8051U.H" #include "intrins.h" #include "stdio.h" typedef unsigned char u8; // 8 bits typedef unsigned int u16; // 16 bits typedef unsigned long u32; // 32 bits #define FOSC 11059200UL //定义为无符号长整型,避免计算溢出 #define BRT (65536 - (FOSC / 115200+2) / 4) //加 2 操作是为了让 Keil 编译器,自动实现四舍五入运算 bit busy1; char wptr1; char rptr1; char buffer1[64]; // 初始化定时器2 void Timer2Init(void) { T2L = BRT; T2H = BRT >> 8; S1BRT = 1; // S1BRT:串口 1 波特率发生器选择位, 0:选择定时器 1 作为波特率发生器, 1:选择定时器 2 作为波特率发生器(默认值) T2x12 = 1; T2R = 1; } // 串口1 //--------------------------------------------------------------- // 串口1的中断处理 void Uart1Isr() interrupt 4 { if (TI) { TI = 0; busy1 = 0; } if (RI) { RI = 0; buffer1[wptr1++] = SBUF; wptr1 &= 0x0f; } } // 初始化串口1 void Uart1Init() { SCON = 0x50; // wptr1 = 0x00; rptr1 = 0x00; busy1 = 0; P_SW1 |= 0x40; // 选择P3.6,P3.7,因为擎天柱开发板上不提供P3.0,P3.1引脚 } // 向串口1输出一个字符 void Uart1Send(char dat) { while (busy1); busy1 = 1; SBUF = dat; } // 向串口1输出一个字符串 void Uart1SendStr(char *p) { while (*p) { Uart1Send(*p++); } } // 初始化ADC void AdcInit(void) { // 设置ADC, P1.0:高阻输入 P1M0 = 0x00; //设置 P1.0 为 ADC 口 P1M1 = 0x01; ADCTIM = 0x3f; //设置 通道选择时间、保持时间、采样时间 ADCCFG = 0x2f; //设置 ADC 时钟为系统时钟/2/16/16; 转换结果右对齐 ADC_POWER = 1; //使能 ADC 模块 } // 延时 void delay_ms(u16 ms) { u16 i; do { i = FOSC/3000; while(--i); } while(--ms); } //======================================================================== // 函数: u16 Get_ADC12bitResult(u8 channel))//channel = 0~15 // 描述: 查询法读一次ADC结果. // 参数: channel: 选择要转换的ADC, 0~15. // 返回: 12位ADC结果. // 版本: V1.0, 2016-4-28 //======================================================================== u16Get_ADC12bitResult(u8 channel) { ADC_RES = 0; ADC_RESL = 0; ADC_CONTR = (ADC_CONTR & 0xf0) | channel; //设置ADC转换通道 ADC_START = 1; // 启动ADC转换 _nop_(); _nop_(); _nop_(); while (!ADC_FLAG); //查询 ADC 完成标志 ADC_FLAG = 0; return (((u16)ADC_RES << 8) | ADC_RESL); } /*********************************** 查询方式做一次ADC, chn为通道号, chn=0~7对应P1.0~P1.7, chn=8~14对应P0.0~P0.6, chn=15对应BandGap电压. ***********************************/ u16 ADC_convert(u8 chn) { u16val; Get_ADC12bitResult(chn);//参数i=0~15,查询方式做一次ADC, 切换通道后第一次转换结果丢弃. 避免采样电容的残存电压影响. Get_ADC12bitResult(chn);//参数i=0~15,查询方式做一次ADC, 切换通道后第二次转换结果丢弃. 避免采样电容的残存电压影响. val = Get_ADC12bitResult(chn); return val; } u16 adc_val = 0; bit adc_ok_flag = 0; void ADC_Isr() interrupt 5 { ADC_FLAG = 0; //清中断标志 adc_val = ((u16)ADC_RES << 8) | ADC_RESL; adc_ok_flag = 1; // ADC完成标志 ADC_START = 1; //继续 AD 转换 } void main() { unsigned int val = 0; // ADC转换值 float v1=0.0; // 电压值 unsigned char str[32]={0}; EAXFR = 1; //允许访问扩展的特殊寄存器,XFR //(32 位模式请使用这句,注释下一句) // P_SW2 |= 0x80; //允许访问扩展的特殊寄存器,XFR //(8 位模式请使用这句,注释上一句) WTST = 0; //设置取程序代码等待时间, //赋值为 0 表示不等待,程序以最快速度运行 CKCON = 0; //设置访问片内的 xdata 速度, //赋值为 0 表示用最快速度访问,不增加额外的等待时间 P0M0 = 0x00; P0M1 = 0x00; P1M0 = 0x00; P1M1 = 0x00; P2M0 = 0x00; P2M1 = 0x00; P3M0 = 0x00; P3M1 = 0x00; P4M0 = 0x00; P4M1 = 0x00; P5M0 = 0x00; P5M1 = 0x00; Timer2Init(); Uart1Init(); AdcInit(); delay_ms(10); ES = 1; EADC = 1; //使能 ADC 中断 EA = 1; Uart1SendStr("Start Test !\r\n"); ADC_START = 1; //继续 AD 转换 while (1) { if (adc_ok_flag) { val = adc_val; sprintf(str, "ADC Val = %d ", val); Uart1SendStr(str); v1 = (float)5 * val / 4096; sprintf(str, ", Input Vol = %f \r\n", v1); Uart1SendStr(str); delay_ms(500); } } }
中断模式下,为了继续执行ADC采集、转换,需要在中断处理中主动启动ADC转换。
本次测试换了一个电位器模块,效果很好,更加稳定了。