数控电源学习-软件代码分解学习
由ADC端口采样拓扑结构,我们可以获得很多的信息.第一,该ADC端口采样位数为10位,这一指标直接影响ADC采样的精度,专业术语为ADC采样分辨率,由该技术指标,我们可以计算出数控电源能采样的最小输入电压.因为单片机为5V供电,则ADC采样口最大输入电压为5V,根据Vadc(min)=5*(1/2^10)=4.88mV,则该单片机能分辨的最小输入电压为4.88mV.
根据上图我们能获得的第二个信息是,该型号的单片机具有ADC采样功能的端口为P1.0口,所以当电路中需要进行ADC采样时,需要使用P1.0口.
获得的第三个信息是,能控制ADC采样口的特殊功能寄存器包括ADC_POWER(ADC电源控制寄存器),SPEED1,SPEED0(ADC采样速度寄存器),ADC_FLAG(ADC转换结束标志位),ADC_START(ADC转换启动控制位),CHS2,CHS1,CHS0(模拟通道选择控制位)等.
获得的第四个信息是ADC采样数据保存寄存器包括ADC_RES,ADC_RESL.由该寄存器的使用,我们就知道所谓10位ADC采样的来源,其实就是两个8位的寄存器组合起来.不过有一个寄存器只使用2位,另一个寄存器使用8位,具体哪个寄存器使用2位,哪个寄存器使用8位是由另一个寄存器CLK_DIV中的ADRJ位控制的.
至此该单片机的ADC采样端口控制寄存器已经讲述完毕,大家可以参考STC的技术手册,上述有详细的讲解.
接着上乐工的程序,按照乐工的源代码,首先是ADC端口的宏定义,这个没有什么疑问.
#define ADC_POWER 0X80//ADC电源控制位
#define ADC_FLAG 0X10//ADC转换结束标志
#define ADC_START 0X08//ADC转换启动控制位
#define ADC_SPEEDLL 0X00//ADC转换速度540个时钟周期
#define ADC_SPEEDL 0x20//ADC转换速度360个时钟周期
#define ADC_SPEEDH 0x40//ADC转换速度180个时钟周期
#define ADC_SPEEDHH 0x60//ADC转换速度90个时钟周期
#define ADC_CH7 0X07//ADC转换位数
接着是功能函数的申明
void InitAdc();//ADC端口初始化
uint GetAdc10BitResult(uchar channel);//ADC采样数据读取
再其次是各功能函数
ADC端口初始化
void InitAdc()
{
P1ASF=0XFF;
//设置P1口为AD口ADC_RES=0X00;//AD转化寄存器清零
ADC_RESL=0X00;//AD转化寄存器清零
ADC_CONTR=ADC_POWER|ADC_SPEEDLL;
//打开AD电源,确定AD转换速率delay_1ms(1);
}
上述程序都没有问题,但是下面的ADC采样数据读取,我就有疑问了,希望乐工以及群里的高手能解答一下,谢谢.
//读取ADC采样结果 uint GetAdc10BitResult(uchar AdcNum) { uint AdcResult; uchar i; if(AdcNum>ADC_CH7)//ADC采样位数判断 return 1024; ADC_RES=0X00;//AD转化寄存器清零 ADC_RESL=0X00;//AD转化寄存器清零 ADC_CONTR=(ADC_CONTR&0XE0)|ADC_START|AdcNum; _nop_(); _nop_(); _nop_(); _nop_(); for(i=0;i<250;i++) { if(ADC_CONTR&ADC_FLAG) { ADC_CONTR=ADC_CONTR&(~ADC_FLAG); if(PCON2&(0X01<<5)) { AdcResult=(uint)(ADC_RES&0X03); AdcResult=(AdcResult<<8)|ADC_RESL; } else { AdcResult=(uint)(ADC_RES); AdcResult=(AdcResult<<2)|(ADC_RESL&0X03); } return AdcResult; } } return 1024; }
这里的疑问是ADC_CH7等于7,也就是当ADC_NUM大于7是,就不执行下面的代码,该功能函数直接返回1024,我有两个疑问,第一为什么采样的位数大于7就不执行,为什么不是大于10.第二个疑问是返回值为什么是1024,不是0.
第二个疑问是下面这段代码
为什么i需要小于250,这段代码因为没有搞懂它的作用,所以后面的代码看的云里雾里,不知道是是什么作用,希望大家能解答,谢谢.今天我就讲到这里,我把我懂得东西贴出来,把我不懂的东西也贴出来,希望大家能指点指点我.
将重新整理注释的ADC采样代码上传,供大家参考,其实还是乐工的功劳,我自己只是把它贴出来用于消化而已
#include "Ad.h" #include "Delay.h" //ADC端口初始化 void InitAdc() { P1ASF=0XFF;//设置P1口为AD口 ADC_RES=0X00;//AD转化寄存器清零 ADC_RESL=0X00;//AD转化寄存器清零 ADC_CONTR=ADC_POWER|ADC_SPEEDLL;//打开AD电源,确定AD转换速率 delay_1ms(1); } //读取ADC采样结果 uint GetAdc10BitResult(uchar AdcNum) { uint AdcResult; uchar i; if(AdcNum>ADC_CH7)//ADC采样位数判断 return 1024; ADC_RES=0X00;//AD转化寄存器清零 ADC_RESL=0X00;//AD转化寄存器清零 ADC_CONTR=(ADC_CONTR&0XE0)|ADC_START|AdcNum;//ADC寄存器配置 _nop_(); _nop_(); _nop_(); _nop_(); for(i=0;i<250;i++)// { if(ADC_CONTR&ADC_FLAG)//数模转换结束 { ADC_CONTR=ADC_CONTR&(~ADC_FLAG);//ADC采样寄存器置零 if(PCON2&(0X01<<5))//ADRJ为1 { AdcResult=(uint)(ADC_RES&0X03);//采样数据高两位保存于ADC_RES AdcResult=(AdcResult<<8)|ADC_RESL;//采样数据低八位保存于ADC_RESL } else { AdcResult=(uint)(ADC_RES);//采样数据低八位保存于ADC_RES AdcResult=(AdcResult<<2)|(ADC_RESL&0X03);//采样数据高两位保存于ADC_RESL } return AdcResult; } } return 1024; }
第一个寄存器为IAP_DATA,作用为保存读写数据.
第二个寄存器为IAP_ADDRH,IAP_ADDRL,作用为ISP_IAP操作时的地址寄存器高八位和低八位
第三个寄存器是ISP_IAP命令寄存器IAP_CMD,作用是对EEPROM中的数据存储区进行读,编程,擦除操作
第四个寄存器为IAP_TRIG,当需要进行IAP操作时,都需要先将IAP_FLAG写入5A再写入A5,才能接着进行IAP操作,该操作看手册我没有看的明白,需要结合实际的代码来分析
第五个寄存器为IAP_CONTR,为IAP控制寄存器,用于控制IAP的使能,复位起始地址,读,擦除,编程时间等,该寄存器是EEPROM中比较关键的寄存器之一
第六个寄存器为电源控制寄存器PCON,当检测到电源电压过低时,不进行EEPROM_IAP操作
首先依旧是老套路,寄存器的宏定义
sfr IAP_DATA = 0xC2; sfr IAP_ADDRH = 0xC3; sfr IAP_ADDRL = 0xC4; sfr IAP_CMD = 0xC5; sfr IAP_TRIG = 0xC6; sfr IAP_CONTR = 0xC7; sfr ISP_DATA = 0xC2; sfr ISP_ADDRH = 0xC3; sfr ISP_ADDRL = 0xC4; sfr ISP_CMD = 0xC5; sfr ISP_TRIG = 0xC6; sfr ISP_CONTR = 0xC7; #define CMD_IDLE 0X00//空闲模式 #define CMD_READ 0X01//Iap字节读命令 #define CMD_PROGRAM 0X02//Iap字节编程命令 #define CMD_ERASE 0X03//Iap扇区擦除命令 #define ENABLE_IAP 0X80//Iap使能设置 //#define ENABLE_IAP 0x80//if SYSCLK<30MHz //#define ENABLE_IAP 0x81//if SYSCLK<24MHz //#define ENABLE_IAP 0x82//if SYSCLK<20MHz //#define ENABLE_IAP 0x83//if SYSCLK<12MHz //#define ENABLE_IAP 0x84//if SYSCLK<6MHz //#define ENABLE_IAP 0x85//if SYSCLK<3MHz //#define ENABLE_IAP 0x86//if SYSCLK<2MHz //#define ENABLE_IAP 0x87//if SYSCLK<1MHz #define SetVoltageAddress 0X0005// #define SetCurrentAddress 0X0205//
接着是各功能函数的申明
void IapIdle();//关闭Iap功能 void IapEraseSector(uint addr);//扇区擦除 uchar IapReadByte(uint addr);//从Isp/Iap/EEPROM中读取一字节 IapReadByte16BitData(uint adr);//从Isp/Iap/EEPROM中读取两字节 void IapProgramByte(uint addr,uchar dat);//向Isp/Iap/EEPROM中写一字节 void IapProgram16BitData(uint adr, uint dat);////向Isp/Iap/EEPROM中写两字节
接下来是各功能函数的具体实现,乐工参考了STC单片机的例程,我把乐工的代码贴出来同时把我的疑问也写出来,希望大家指点,谢谢先.
首先是
void IapIdle()//关闭Iap功能 { IAP_CONTR=0X00;//关闭IAP功能 IAP_CMD=0X00;//清除命令寄存器 IAP_TRIG=0X00;//清除触发寄存器 IAP_ADDRH=0X80;//将地址设置到非IAP区域 IAP_ADDRL=0X00; }
在该函数中IAP_ADDRH=0X80;//将地址设置到非IAP区域
IAP_ADDRL=0X00;
这里没有看懂,不知道将地址设置到非IAP区域是什么意思,查阅STC技术手册,看到是否与IAP字节读时EEPROM结束扇区末尾地址有关,望乐工解答,先谢谢乐工.
接着是扇区擦除操作代码
void IapEraseSector(uint IapAddress0)//扇区擦除 { IAP_CONTR=ENABLE_IAP;//使能IAP IAP_CMD=CMD_ERASE;//设置IAP命令 IAP_ADDRL=IapAddress0;//设置IAP低地址 IAP_ADDRH=IapAddress0>>8;//设置IAP高地址 IAP_TRIG=0x5a;//写触发命令(0x5a) IAP_TRIG=0xa5;//写触发命令(0xa5) _nop_();//等待ISP/IAP/EEPROM操作完成 IapIdle(); }
接着是读一字节数据
//从Isp/Iap/EEPROM中读取一字节 uchar IapReadByte(uint IapAddress1) { uchar BufferData0;//缓冲数据 IAP_CONTR=ENABLE_IAP;//使能IAP IAP_CMD=CMD_READ;//设置IAP命令 IAP_ADDRL=IapAddress1;//设置IAP低地址 IAP_ADDRH=IapAddress1>>8;//设置IAP高地址 IAP_TRIG=0x5a;//写触发命令(0x5a) IAP_TRIG=0xa5;//写触发命令(0xa5) _nop_();//等待ISP/IAP/EEPROM操作完成 BufferData0=IAP_DATA;//读ISP/IAP/EEPROM数据 IapIdle();//关闭IAP功能 return BufferData0; //返回 }
//从Isp/Iap/EEPROM中读取两字节 IapReadByte16BitData(uint IapAddress2) { uint BufferData16Bit;//缓冲数据 BufferData16Bit=IapReadByte(IapAddress2); BufferData16Bit<<=8; IapAddress2++; BufferData16Bit=BufferData16Bit|IapReadByte(IapAddress2); return BufferData16Bit;}
该代码中IapAddress2++作用是否是将该数值加一后用于存储下一段数据
//向Isp/Iap/EEPROM中写一字节 void IapProgramByte(uint IapAddress3,uchar BufferData1) { IAP_CONTR=ENABLE_IAP;//使能IAP IAP_CMD=CMD_PROGRAM;//设置IAP命令 IAP_ADDRL=IapAddress3;//设置IAP低地址 IAP_ADDRH=IapAddress3>>8;//设置IAP高地址 IAP_DATA=BufferData1;//写ISP/IAP/EEPROM数据 IAP_TRIG=0x5a;//写触发命令(0x5a) IAP_TRIG=0xa5;//写触发命令(0xa5) _nop_();//等待ISP/IAP/EEPROM操作完成 IapIdle(); } //向Isp/Iap/EEPROM中写两字节 void IapProgram16BitData(uint IapAddress4, uint BufferData16Bit) { uchar DataTemp1,DataTemp2; DataTemp1=BufferData16Bit/256; DataTemp2=BufferData16Bit%256; IapProgramByte(IapAddress4, DataTemp1); IapAddress4++; IapProgramByte(IapAddress4, DataTemp2); }
这几段代码的整理以及消化花的时间不多,相对于昨天ADC采样来说,简单了一点.
这里我很感谢也很感激乐工,每次我有问题,他都能及时给我解答给我帮助,我很感谢乐工.谢谢
STC15系列自带六路PWM输出,功能还是比较强大的.首先我们还是先要认识一下PWM输出功能寄存器的配置.
第一个寄存器为P_SW2,端口配置寄存器.左右为用于扩展SFR访问控制使能,说的直白一点就是,如果单片机想访问外部特殊功能寄存器SFR或者外部RAM,必须先将设置该寄存器.需要补充一点,外部功能寄存器不一定在单片机外部.因为现在单片机的功能比较强大,内部集成了很多的模块,所谓外部设备只是相对于内部的RAM和SFR.其实说穿了就是单片机访问的地址不同而已.
第二个寄存器为PWMCFG,PWM配置寄存器,该寄存器还是比较重要的.主要作用有两个.第一个当PWM计数器归零时是否触发ADC转换功能.第二个作用是设置PWM输出口的初始电平.
第三个寄存器为PWMCR,PWM控制寄存器.该寄存器的作用也比较重要,主要作用为设置单片机IO口的状态,如果为0则为普通的单片机IO口(GPIO),如果为1则为PWM输出口.该寄存器说穿了就是用于控制单片机IO的PWM功能的开启与关闭.
第四个寄存器为PWMIF,PWM中断标志寄存器,主要作用是当PWM发生翻转时,通过该寄存器的设置,程序会进入相应的中断程序执行相应的程序.
第五个寄存器为PWMFDCR,PWM外部异常控制寄存器,该寄存器的作用是当PWM输出口外部发生异常时(比如短路,过载,过压等特殊情况),通过该寄存器的配置可以使PWM输出口变为高阻态输入口.
第六个寄存器为PWM计数器,分为PWMCH和PWMCL,通过PWMCH和PWMCL来设置PWM输出的周期.
第七个寄存器为PWMCKS,PWM时钟选择寄存器.作用很简单,用于时钟源的选择以及预分频作用.
第八个寄存器为PWM2翻转计数器,主要用于设置PWM波形翻转的时刻.
上述寄存器为STC15W4K系列的PWM输出寄存器,上述寄存器的配置主要作用是控制IO口输出PWM波形以及输出的周期等
针对上述PWM输出的寄存器配置,我想说说自己的理解.
其实上述寄存器配置貌似很多,细细想想,一点都不复杂.大家如果觉得不好理解,可以结合普通的PWM波形理解.对于普通的PWM波形,需要考虑的因素包括PWM的周期,导通和关闭的时间等.结合到单片机的PWM输出,其实大同小异.首先是周期,那需要涉及的东西就是时钟源,分频器.考虑到高低电平的时间,那就需要通过计数器来实现.再加上单片机IO的输出状态功能,那就需要涉及到IO口的功能配置等.最后当单片机PWM输出发生翻转时,是否需要执行其他的动作,那就需要涉及到中断,这是我的理解,大致上单片机IO的PWM输出配置就是这些功能.
接下来我们上乐工的代码,通过乐工的代码来看看具体的寄存器的配置
#include"PWM10Bit.H" //PWM配置函数 // 参数: none // 返回: none void PWM10BitConfig(void) { u8 xdata *px; EAXSFR();//访问XFR P_SW2 |= 0x80 PWMCR|=0x02;//PWM通道3为PWM输出口,受PWM波形发生器控制 PWMCFG|=0x02;//PWM3输出口初始电平1 P21=0;//端口P2^1 P2n_push_pull(0x01<<1);//P2^1口为强推挽输出,P2M1&=~(bitn),P2M0|=(bitn)为01 PWMCR|=0x04;//PWM通道4为PWM输出口,受PWM波形发生器控制 PWMCFG|=0x04;//PWM4输出口初始电平1 P22=1;//端口P2^1 P2n_push_pull(0x01<<2);//P2^2口为强推挽输出,P2M1&=~(bitn),P2M0|=(bitn)为01 px=PWMCH;//PWM计数器的高字节 *px=1023/256; // *px=4095/256; px++; *px=1023%256;//PWM计数器的低字节 // *px=4095%256;//PWM计数器的低字节 px++;//PWMCKS,PWM时钟选择 *px=PwmClk_1T;//时钟源: PwmClk_1T,PwmClk_2T, ... PwmClk_16T, PwmClk_Timer2 EAXRAM();//恢复访问XRAM P_SW2 &= ~0x80 PWMCR|=ENPWM;//使能PWM波形发生器,PWM计数器开始计数 PWMCR |= ENPWM; PWMCR|=ECBI;// 允许PWM计数器归零中断 }
上述代码中,乐工申明了一个指针型变量*PX, 该变量的类型为整型,存储区域为xdata.解释一下xdata,所谓xdata只是用于指明该变量的保存区域为外部数据存储区.这里大家有疑问的地方可能第一个就是xdata这里.其实单片机一般的数据默认存储区是通过编译器来配置的(以keil为例).C51有三种存储模式,分别为小模式,紧凑型模式和大模式.小模式对应存储类型为data型,紧凑型模式对应存储类型为pdata,大模式对应存储类型为xdata(该资料我引用与单片机原理及应用(黄勤主编)).
接下来EAXSFR()这段代码的作用为配置单片机的操作对象为扩展SFR区域,大家可以参考STC技术手册.
在上述的代码段中主要是用于PWM口的配置,没有什么难点.
最后这段关于PWM中断函数我觉得还是比较重要的,也是乐工整个项目真正的难点以及核心所在
//PWM中断函数 void PWM_int(void) interrupt PWM_VECTOR { u8 xdata *px; u8 SW2_tmp; if(PWMIF&CBIF)//PWM计数器归零中断标志 { PWMIF&=~CBIF;//清除中断标志 SW2_tmp=P_SW2;//保存SW2设置 EAXSFR();//访问XFR,P_SW2|=0x80,访问扩展RAM中的寄存器必须EAXSFR=1 px=PWM3T2H; *px=OUT_Current_PWM/256; px++; *px=OUT_Current_PWM%256; px=PWM4T2H; *px=OUT_Voltage_PWM/256;//OUT_Current_PWM px++; *px=OUT_Voltage_PWM%256; P_SW2 = SW2_tmp;//恢复SW2设置 } }