• 回复
  • 收藏
  • 点赞
  • 分享
  • 发新帖

数控电源学习-软件代码分解学习

今天   准备着手将乐工的开源项目数控电源的代码重新梳理一遍   将有疑问的地方贴出来   希望大家能一起学习  高手们希望能及时指导我们这些菜鸟    准备火力全开   同时晚上抽空把电源管理芯片494以及输出滤波电感的计算也讲解讲解   吼吼
全部回复(48)
正序查看
倒序查看
2015-04-09 20:47

    首先从AD采样端口讲起,目前乐工的数控电源,AD采样端口采用的是P1.1和P1.2.这两个端口一个用于输出电压采样,一个用于输出电流采样.

硬件原理图很简单,真正的重点是ADC端口的配置以及端口数据采集转换的流程.

    正式开始,乐工的数控电源采用的单片机型号为STC15W4K16S4,该型号的单片机目前是STC公司最强的单片机.查找STC技术手册,在ADC一章,可以看到ADC采样端口内部的拓扑结构,最上面为ADC各功能寄存器,最左边为具有ADC采样功能的单片机端口,中间为比较器以及DAC转换模块,再往右为逐次比较寄存器,是该ADC端口采样的方式,最右边为ADC采样结果保存寄存器.

 

0
回复
2015-04-09 21:12
@hello-no1
  首先从AD采样端口讲起,目前乐工的数控电源,AD采样端口采用的是P1.1和P1.2.这两个端口一个用于输出电压采样,一个用于输出电流采样.[图片]硬件原理图很简单,真正的重点是ADC端口的配置以及端口数据采集转换的流程.  正式开始,乐工的数控电源采用的单片机型号为STC15W4K16S4,该型号的单片机目前是STC公司最强的单片机.查找STC技术手册,在ADC一章,可以看到ADC采样端口内部的拓扑结构,最上面为ADC各功能寄存器,最左边为具有ADC采样功能的单片机端口,中间为比较器以及DAC转换模块,再往右为逐次比较寄存器,是该ADC端口采样的方式,最右边为ADC采样结果保存寄存器.[图片] 

  由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的技术手册,上述有详细的讲解.

0
回复
2015-04-09 21:17
@hello-no1
 由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);

}

0
回复
2015-04-09 21:28
@hello-no1
接着上乐工的程序,按照乐工的源代码,首先是ADC端口的宏定义,这个没有什么疑问.#defineADC_POWER 0X80//ADC电源控制位#defineADC_FLAG  0X10//ADC转换结束标志#defineADC_START 0X08//ADC转换启动控制位#defineADC_SPEEDLL0X00//ADC转换速度540个时钟周期#defineADC_SPEEDL 0x20//ADC转换速度360个时钟周期#defineADC_SPEEDH 0x40//ADC转换速度180个时钟周期#defineADC_SPEEDHH0x60//ADC转换速度90个时钟周期#defineADC_CH7  0X07//ADC转换位数接着是功能函数的申明voidInitAdc();//ADC端口初始化uintGetAdc10BitResult(ucharchannel);//ADC采样数据读取再其次是各功能函数ADC端口初始化voidInitAdc(){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;
}

0
回复
2015-04-09 21:40
@hello-no1
上述程序都没有问题,但是下面的ADC采样数据读取,我就有疑问了,希望乐工以及群里的高手能解答一下,谢谢.//读取ADC采样结果uintGetAdc10BitResult(ucharAdcNum){uintAdcResult;uchari;if(AdcNum>ADC_CH7)//ADC采样位数判断return1024;ADC_RES=0X00;//AD转化寄存器清零ADC_RESL=0X00;//AD转化寄存器清零ADC_CONTR=(ADC_CONTR&0XE0)|ADC_START|AdcNum;_nop_();_nop_();_nop_();_nop_();for(i=0;i
在上述的程序中,第一个疑问是

这里的疑问是ADC_CH7等于7,也就是当ADC_NUM大于7是,就不执行下面的代码,该功能函数直接返回1024,我有两个疑问,第一为什么采样的位数大于7就不执行,为什么不是大于10.第二个疑问是返回值为什么是1024,不是0.

第二个疑问是下面这段代码

为什么i需要小于250,这段代码因为没有搞懂它的作用,所以后面的代码看的云里雾里,不知道是是什么作用,希望大家能解答,谢谢.今天我就讲到这里,我把我懂得东西贴出来,把我不懂的东西也贴出来,希望大家能指点指点我.

0
回复
2015-04-10 09:41
@hello-no1
在上述的程序中,第一个疑问是[图片]这里的疑问是ADC_CH7等于7,也就是当ADC_NUM大于7是,就不执行下面的代码,该功能函数直接返回1024,我有两个疑问,第一为什么采样的位数大于7就不执行,为什么不是大于10.第二个疑问是返回值为什么是1024,不是0.第二个疑问是下面这段代码[图片]为什么i需要小于250,这段代码因为没有搞懂它的作用,所以后面的代码看的云里雾里,不知道是是什么作用,希望大家能解答,谢谢.今天我就讲到这里,我把我懂得东西贴出来,把我不懂的东西也贴出来,希望大家能指点指点我.
上进好童鞋 加油!~
0
回复
2015-04-10 11:05
@hello-no1
在上述的程序中,第一个疑问是[图片]这里的疑问是ADC_CH7等于7,也就是当ADC_NUM大于7是,就不执行下面的代码,该功能函数直接返回1024,我有两个疑问,第一为什么采样的位数大于7就不执行,为什么不是大于10.第二个疑问是返回值为什么是1024,不是0.第二个疑问是下面这段代码[图片]为什么i需要小于250,这段代码因为没有搞懂它的作用,所以后面的代码看的云里雾里,不知道是是什么作用,希望大家能解答,谢谢.今天我就讲到这里,我把我懂得东西贴出来,把我不懂的东西也贴出来,希望大家能指点指点我.
帖子不错哦,推荐到帖子底部经典图库。更多请点击:http://www.dianyuan.com/bbs/classic/
0
回复
2015-04-10 11:57
@hello-no1
在上述的程序中,第一个疑问是[图片]这里的疑问是ADC_CH7等于7,也就是当ADC_NUM大于7是,就不执行下面的代码,该功能函数直接返回1024,我有两个疑问,第一为什么采样的位数大于7就不执行,为什么不是大于10.第二个疑问是返回值为什么是1024,不是0.第二个疑问是下面这段代码[图片]为什么i需要小于250,这段代码因为没有搞懂它的作用,所以后面的代码看的云里雾里,不知道是是什么作用,希望大家能解答,谢谢.今天我就讲到这里,我把我懂得东西贴出来,把我不懂的东西也贴出来,希望大家能指点指点我.
ADC_CH7等于7:AD通道只有8个即0-7软件滤出错误,1024其实是自定义的一个数值,10位AD的有效数据为0-1023   只要是1023以上的任意数据都可以,只要不是0-1023软件直接排除错误返回了。
0
回复
2015-04-10 12:04
@yueyunno1
ADC_CH7等于7:AD通道只有8个即0-7软件滤出错误,1024其实是自定义的一个数值,10位AD的有效数据为0-1023 只要是1023以上的任意数据都可以,只要不是0-1023软件直接排除错误返回了。
i=250这个for循环可要可不要
0
回复
2015-04-10 12:27
@电源网-fqd
帖子不错哦,推荐到帖子底部经典图库。更多请点击:http://www.dianyuan.com/bbs/classic/
今晚着手整理EEPROM代码,我到时把我理解的以及我不懂的都贴出来,希望高手锰能及时指点,谢谢诸位
0
回复
2015-04-10 20:10
@yueyunno1
i=250这个for循环可要可不要

将重新整理注释的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;
}

0
回复
2015-04-10 20:33
@hello-no1
将重新整理注释的ADC采样代码上传,供大家参考,其实还是乐工的功劳,我自己只是把它贴出来用于消化而已#include"Ad.h"#include"Delay.h"//ADC端口初始化voidInitAdc(){P1ASF=0XFF;//设置P1口为AD口ADC_RES=0X00;//AD转化寄存器清零ADC_RESL=0X00;//AD转化寄存器清零ADC_CONTR=ADC_POWER|ADC_SPEEDLL;//打开AD电源,确定AD转换速率delay_1ms(1);}//读取ADC采样结果uintGetAdc10BitResult(ucharAdcNum){uintAdcResult;uchari;if(AdcNum>ADC_CH7)//ADC采样位数判断return1024;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
    今天主要是分解乐工的EEPROM代码,还是老套路,先熟悉STC15W4K16S4芯片EEPROM的特殊功能寄存器.

第一个寄存器为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操作

0
回复
2015-04-10 20:42
@hello-no1
  今天主要是分解乐工的EEPROM代码,还是老套路,先熟悉STC15W4K16S4芯片EEPROM的特殊功能寄存器.[图片]第一个寄存器为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//

0
回复
2015-04-10 20:44
@hello-no1
首先依旧是老套路,寄存器的宏定义sfrIAP_DATA=0xC2;sfrIAP_ADDRH=0xC3;sfrIAP_ADDRL=0xC4;sfrIAP_CMD=0xC5;sfrIAP_TRIG=0xC6;sfrIAP_CONTR=0xC7;sfrISP_DATA=0xC2;sfrISP_ADDRH=0xC3;sfrISP_ADDRL=0xC4;sfrISP_CMD=0xC5;sfrISP_TRIG=0xC6;sfrISP_CONTR=0xC7;#defineCMD_IDLE0X00//空闲模式#defineCMD_READ0X01//Iap字节读命令#defineCMD_PROGRAM0X02//Iap字节编程命令#defineCMD_ERASE0X03//Iap扇区擦除命令#defineENABLE_IAP0X80//Iap使能设置//#defineENABLE_IAP0x80//ifSYSCLK

接着是各功能函数的申明

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中写两字节

0
回复
2015-04-10 20:51
@hello-no1
接着是各功能函数的申明voidIapIdle();//关闭Iap功能voidIapEraseSector(uintaddr);//扇区擦除ucharIapReadByte(uintaddr);//从Isp/Iap/EEPROM中读取一字节IapReadByte16BitData(uintadr);//从Isp/Iap/EEPROM中读取两字节voidIapProgramByte(uintaddr,uchardat);//向Isp/Iap/EEPROM中写一字节voidIapProgram16BitData(uintadr,uintdat);////向Isp/Iap/EEPROM中写两字节
        函数申明这里我有疑问了,IapReadByte16BitData(uint adr)该函数的申明,乐工并没有指明函数的返回值类型,根据C语言的规定,如果没有指定函数返回值类型,则返回int型数据.但是该函数是通过调用uchar IapReadByte(uint addr)函数来实现读取两字节数据的.我的疑问是IapReadByte(uint addr)的返回值为uchar型,但是IapReadByte16BitData(uint adr)函数的返回值是int型,是否有错误?
0
回复
2015-04-10 21:04
@hello-no1
        函数申明这里我有疑问了,IapReadByte16BitData(uintadr)该函数的申明,乐工并没有指明函数的返回值类型,根据C语言的规定,如果没有指定函数返回值类型,则返回int型数据.但是该函数是通过调用ucharIapReadByte(uintaddr)函数来实现读取两字节数据的.我的疑问是IapReadByte(uintaddr)的返回值为uchar型,但是IapReadByte16BitData(uintadr)函数的返回值是int型,是否有错误?

接下来是各功能函数的具体实现,乐工参考了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结束扇区末尾地址有关,望乐工解答,先谢谢乐工.

0
回复
2015-04-10 21:05
@hello-no1
接下来是各功能函数的具体实现,乐工参考了STC单片机的例程,我把乐工的代码贴出来同时把我的疑问也写出来,希望大家指点,谢谢先.首先是voidIapIdle()//关闭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();
}

0
回复
2015-04-10 21:08
@hello-no1
接着是扇区擦除操作代码voidIapEraseSector(uintIapAddress0)//扇区擦除{IAP_CONTR=ENABLE_IAP;//使能IAPIAP_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;                     //返回
}

0
回复
2015-04-10 21:14
@hello-no1
接着是读一字节数据//从Isp/Iap/EEPROM中读取一字节ucharIapReadByte(uintIapAddress1){ucharBufferData0;//缓冲数据IAP_CONTR=ENABLE_IAP;//使能IAPIAP_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功能returnBufferData0;//返回}
//从Isp/Iap/EEPROM中读取两字节
IapReadByte16BitData(uint IapAddress2)
{
	uint BufferData16Bit;//缓冲数据
	BufferData16Bit=IapReadByte(IapAddress2);
	BufferData16Bit<<=8;
	IapAddress2++;
	BufferData16Bit=BufferData16Bit|IapReadByte(IapAddress2);
	return BufferData16Bit;

}

该代码中IapAddress2++作用是否是将该数值加一后用于存储下一段数据

0
回复
2015-04-10 21:15
@hello-no1
//从Isp/Iap/EEPROM中读取两字节IapReadByte16BitData(uintIapAddress2){uintBufferData16Bit;//缓冲数据BufferData16Bit=IapReadByte(IapAddress2);BufferData16Bit
//向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);
}
0
回复
2015-04-10 21:16
@hello-no1
//向Isp/Iap/EEPROM中写一字节voidIapProgramByte(uintIapAddress3,ucharBufferData1){IAP_CONTR=ENABLE_IAP;//使能IAPIAP_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中写两字节voidIapProgram16BitData(uintIapAddress4,uintBufferData16Bit){ucharDataTemp1,DataTemp2;DataTemp1=BufferData16Bit/256;DataTemp2=BufferData16Bit%256;IapProgramByte(IapAddress4,DataTemp1);IapAddress4++;IapProgramByte(IapAddress4,DataTemp2);}

这几段代码的整理以及消化花的时间不多,相对于昨天ADC采样来说,简单了一点.

这里我很感谢也很感激乐工,每次我有问题,他都能及时给我解答给我帮助,我很感谢乐工.谢谢

0
回复
2015-04-11 00:01
@hello-no1
这几段代码的整理以及消化花的时间不多,相对于昨天ADC采样来说,简单了一点.这里我很感谢也很感激乐工,每次我有问题,他都能及时给我解答给我帮助,我很感谢乐工.谢谢
不错~
0
回复
2015-04-12 09:24
@wangchuangwccc
不错~
今天整理PWM函数,等会上传,把不能理解的地方以及理解的东西都贴出来,吼吼
0
回复
2015-04-12 10:05
@hello-no1
今天整理PWM函数,等会上传,把不能理解的地方以及理解的东西都贴出来,吼吼

    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波形以及输出的周期等

0
回复
2015-04-12 10:12
@hello-no1
  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输出配置就是这些功能.

0
回复
2015-04-12 10:14
@hello-no1
    针对上述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计数器归零中断
}

0
回复
2015-04-12 10:57
@hello-no1
接下来我们上乐工的代码,通过乐工的代码来看看具体的寄存器的配置#include"PWM10Bit.H"//PWM配置函数//参数:none//返回:nonevoidPWM10BitConfig(void){u8xdata*px;EAXSFR();//访问XFRP_SW2|=0x80PWMCR|=0x02;//PWM通道3为PWM输出口,受PWM波形发生器控制PWMCFG|=0x02;//PWM3输出口初始电平1P21=0;//端口P2^1P2n_push_pull(0x01

    上述代码中,乐工申明了一个指针型变量*PX,  该变量的类型为整型,存储区域为xdata.解释一下xdata,所谓xdata只是用于指明该变量的保存区域为外部数据存储区.这里大家有疑问的地方可能第一个就是xdata这里.其实单片机一般的数据默认存储区是通过编译器来配置的(以keil为例).C51有三种存储模式,分别为小模式,紧凑型模式和大模式.小模式对应存储类型为data型,紧凑型模式对应存储类型为pdata,大模式对应存储类型为xdata(该资料我引用与单片机原理及应用(黄勤主编)).

    接下来EAXSFR()这段代码的作用为配置单片机的操作对象为扩展SFR区域,大家可以参考STC技术手册.

    在上述的代码段中主要是用于PWM口的配置,没有什么难点.

0
回复
2015-04-12 11:01
@hello-no1
    上述代码中,乐工申明了一个指针型变量*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设置

	}
}

0
回复
2015-04-12 11:06
@hello-no1
最后这段关于PWM中断函数我觉得还是比较重要的,也是乐工整个项目真正的难点以及核心所在//PWM中断函数voidPWM_int(void)interruptPWM_VECTOR{u8xdata*px;u8SW2_tmp;if(PWMIF&CBIF)//PWM计数器归零中断标志{PWMIF&=~CBIF;//清除中断标志SW2_tmp=P_SW2;//保存SW2设置EAXSFR();//访问XFR,P_SW2|=0x80,访问扩展RAM中的寄存器必须EAXSFR=1px=PWM3T2H;*px=OUT_Current_PWM/256;px++;*px=OUT_Current_PWM%256;px=PWM4T2H;*px=OUT_Voltage_PWM/256;//OUT_Current_PWMpx++;*px=OUT_Voltage_PWM%256;P_SW2=SW2_tmp;//恢复SW2设置}}
    该段代码主要用于输出两路互补型SPWM波形,这段代码大家可以参考STC15系列的技术手册中<用STC15W4K系列输出两路互补SWPM>一章,书中有具体的讲解,大家可以看看.针对PWM配置以及输出这一段,我自己本身的疑问并不多.如果说有难点,那就是PWM本身寄存器的配置这里会有一些疑问.
0
回复
2015-04-12 15:02
@hello-no1
    该段代码主要用于输出两路互补型SPWM波形,这段代码大家可以参考STC15系列的技术手册中一章,书中有具体的讲解,大家可以看看.针对PWM配置以及输出这一段,我自己本身的疑问并不多.如果说有难点,那就是PWM本身寄存器的配置这里会有一些疑问.
分析得很透彻,说明你把数字电路的核心理解到了,其实这些程序的实现就是在模拟数字电路,只要把这个思维模式搞懂其他的代码都不是问题了。
0
回复