黄金会员
 
主题
帖子
积分6694
阅读权限40
注册时间2011-3-11
最后登录1970-1-1
在线时间 小时
|

楼主 |
发表于 2019-8-2 11:49
|
显示全部楼层
本帖最后由 pla155 于 2019-8-3 16:55 编辑
关于运算:单字节乘四字节乘法程序简单,由四次单字节乘法和三次加法完成。
SUM_ADC0 * 125 可以一次完成,SUM_ADC1 * 625分成两次,一次125,一次5,都不超过255限制。
乘4 是通过左移位2次实现,目的是通过简单的舍弃后2字节 完成/256/64的计算,而不会引入计算误差。
SUM_ADC0 = (ADCRH,ADCRL) * 64
// Current = SUM_ADC0 / 64 *(2000/4096) = SUM_ADC0 * 125 / 256 / 64
// (MAX = 07 D0 00 00) = SUM_ADC0 * 125 * 4 /256/256
// 舍弃后16位,07D0H = 2000mA
SUM_ADC1 = (ADCRH,ADCRL) * 64
// Voltage = SUM_ADC1 / 64 *(10000/4096) = SUM_ADC1 * 625 / 256 / 64
// = SUM_ADC1 * 625 * 4 / 256 / 256
// (MAX = 27 10 00 00) = SUM_ADC1 * 5 * 125 * 4 / 256 / 256
// 舍弃后16位,2710H = 10000mV
Power = (Current * Voltage) + 0.5 ,保留2位小数
下面是AD采样的子函数文件,被T5定时器调用,完成AD采样和相关数值计算、转换功能。
NAME FUN_ADC
/*****************************************************************************/
#include <.\01LCD\HC89F0411P.H>
/*****************************************************************************/
EXTRN BIT (T5_tick_0)
// - - - - - - - - - - - - -
EXTRN DATA (PCF8820_X,PCF8820_Y,PCF8820_Char)
// - - - - - - - - - - - - - 电流采样值求和结果,每秒64次,最大值 4096 * 64 =04 00 00H
EXTRN DATA (SUM_ADC0_H,SUM_ADC0_M,SUM_ADC0_L)
// - - - - - - - - - - - - - 电压采样每秒64次,累计高字节缓冲字
EXTRN DATA (SUM_ADC1_H,SUM_ADC1_M,SUM_ADC1_L)
// - - - - - - - - - - - - - 电压采样每秒64次,累计高字节缓冲字
EXTRN DATA (Capacity_T,Capacity_H,Capacity_M,Capacity_L)
// - - - - - - - - - - - - - 充放电计时器
EXTRN DATA (Time_Sec , Time_Min , Time_Hor , Time_Day)
// - - - - - - - - - - - - -
EXTRN DATA (BUF0_T,BUF0_H,BUF0_M,BUF0_L)
EXTRN DATA (BUF1_T,BUF1_H,BUF1_M,BUF1_L)
// - - - - - - - - - - - - - 电流运算结果
EXTRN DATA (CurrentH,CurrentL)
// - - - - - - - - - - - - - 电压运算结果
EXTRN DATA (VoltageH,VoltageL)
// - - - - - - - - - - - - - 功率运算结果
EXTRN DATA (PowerH,PowerL)
// - - - - - - - - - - - - -
EXTRN DATA (temp)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EXTRN CODE (LONG_RL1)
EXTRN CODE (LONG_MUL_BYTE,WORD_MUL_WORD)
EXTRN CODE (LONG_DIV_BYTE,WORD_DIV_BYTE)
EXTRN CODE (WORD_To_BCD,SUB_BUF0_1Byte)
EXTRN CODE (SUB_WATCH_BUF)
EXTRN CODE (PCF8820_Char8X16)
/*****************************************************************************/
?PR?Init_ADC?FUN_ADC SEGMENT CODE
?PR?Voltage_To_Ascii?FUN_ADC SEGMENT CODE
?PR?Current_To_Ascii?FUN_ADC SEGMENT CODE
?PR?Power_To_Ascii?FUN_ADC SEGMENT CODE
?PR?Capacity_To_LCD?FUN_ADC SEGMENT CODE
?PR?Time_To_LCD?FUN_ADC SEGMENT CODE
// ------ 下面这两个函数被 T5 中断调用 ---------------------------------------
?PR?AD_Sample?FUN_ADC SEGMENT CODE
?PR?AD_Process?FUN_ADC SEGMENT CODE
/*****************************************************************************/
PUBLIC Init_ADC
PUBLIC Current_To_Ascii , Voltage_To_Ascii , Power_To_Ascii
PUBLIC Capacity_To_LCD , Time_To_LCD
PUBLIC AD_Sample , AD_Process // 这两个函数被 T5 中断调用
/******************************************************************************
* @说明 ADC初始化
* @参数 无
* @返回值 无
* @注 选择外部通道0,转换结果12位数据,高八位置于ADCRH寄存器,ADC时钟16分频
******************************************************************************/
RSEG ?PR?Init_ADC?FUN_ADC
Init_ADC:
;------打开ADC转换电源,选择内部参考电压2伏。因为是3.3V供电
; 位编号 7 6 5 4 3 2 1 0
; 位符号 ADCEN ADCST ADCIF - VREFO VREFS INREF_S[1:0]
MOV ADCC0 , #0X043
;------转换结果12位数据,高八位置于ADCRH寄存器,ADC时钟32分频
; 位编号 7 6 5 4 3 2 1 0
; 位符号 ADCL ALIGN ADCTS[2:0] ADCS[2:0]
;
;ADCL ALIGN ADCRH ADCRL
; 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
;0 0 D11 D10 D9 D8 D7 D6 D5 D4 / / / / D3 D2 D1 D0
;0 1 / / / / D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 选这个方式
;1 0 D11 D10 D9 D8 D7 D6 D5 D4 / / / / / / D3 D2
;1 1 / / / / / / D11 D10 D9 D8 D7 D6 D5 D4 D3 D2
MOV DPTR , #ADCC2
MOV A , #01111111B
MOVX @DPTR , A
RET
/*****************************************************************************/
// ---------- 由T5定时器中断程序调用。 -------------------------------------
// 由T5定时器中断程序调用,每一秒调用128次,交替对ADC0(电流),ADC1(电压)采样64次。
// AD结果是12位,数据右对齐。
// 累加结果存放在两个三字节整数中,最大值 0X04 00 00。
/*****************************************************************************/
RSEG ?PR?AD_Sample?FUN_ADC
AD_Sample: PUSH ACC // 两个通道轮流采样,每秒分别采样2次,通过8毫秒标志位判断
PUSH PSW
JNB T5_tick_0 , JP1_AD_Sample // 转电压采样
MOV A , SUM_ADC0_L
ADD A , ADCRL
MOV SUM_ADC0_L , A
MOV A , SUM_ADC0_M
ADDC A , ADCRH
MOV SUM_ADC0_M , A
CLR A
ADDC A , SUM_ADC0_H
MOV SUM_ADC0_H , A
MOV ADCC1 , #001H //选择下一次转换外部通道1 采样电压
JMP EXIT_AD_Sample
JP1_AD_Sample:
MOV A , SUM_ADC1_L
ADD A , ADCRL
MOV SUM_ADC1_L , A
MOV A , SUM_ADC1_M
ADDC A , ADCRH
MOV SUM_ADC1_M , A
CLR A
ADDC A , SUM_ADC1_H
MOV SUM_ADC1_H , A
MOV ADCC1 , #000H //选择下一次转换外部通道0 采样电流
EXIT_AD_Sample: // --------AD采样数据保存与启动下一轮转换--------
ANL ADCC0 , #11010011B //清除标志位
ORL ADCC0 , #11000011B //启动ADC转换,每秒采样128次,每个通道64次
POP PSW
POP ACC
RET
/*****************************************************************************/
// ---------- 由T5定时器中断程序调用。 -------------------------------------
// 由T5定时器中断程序调用,每一秒调用一次。
// 把SUM_ADC中64次采样累加和进行校准,补偿一个校正数
// 把校准后的SUM_ADC转换成相应量程的测量值,12BIT转化成1.999A , 9.999V 。
/*****************************************************************************/
RSEG ?PR?AD_Process?FUN_ADC
AD_Process: PUSH ACC
PUSH B
PUSH 000H
PUSH PSW
/****************************************/
LCALL Adjust_Current
LCALL Convert_Current
LCALL Adjust_Voltage
LCALL Convert_Voltage
LCALL Convert_Power
// LCALL Convert_Capacity
/****************************************/
LCALL SUB_CLR_SUM
POP PSW
POP 000H
POP B
POP ACC
MOV SBUF , 0C2H//TL5
RET
/*****************************************************************************/
Adjust_Current:
RET
/*****************************************************************************/
Adjust_Voltage:
RET
/*****************************************************************************/
Convert_Current:
// 把存放在 SUM_ADC0 的三字节电流64次采样累加和做平均数,并转化成相应量程数值
// Current = SUM_ADC0 / 64 *(2000/4096) = SUM_ADC0 * 125 / 256 / 64
// (MAX = 07 D0 00 00) = SUM_ADC0 * 125 * 4 /256/256
// 舍弃后16位,07D0 = 2000mA
//-------------------------------------
MOV BUF0_H , SUM_ADC0_H
MOV BUF0_M , SUM_ADC0_M
MOV BUF0_L , SUM_ADC0_L
MOV BUF1_L , #125D
LCALL LONG_MUL_BYTE // SUM_ADC0 * 125
LCALL LONG_RL1
LCALL LONG_RL1 // SUM_ADC0 * 125 * 4
MOV CurrentH , BUF0_T
MOV CurrentL , BUF0_H // SUM_ADC0 * 125 * 4 /256/256
// 通过电流是否为零来计时。故写在这里
MOV A , CurrentH
ADD A , CurrentL
ADDC A , #000H
JZ Exit_Convert_Current // 电流为零,不做累计时间
LCALL Convert_Time
Exit_Convert_Current:
RET
//-------------------------------------
Convert_Voltage:
// 把存放在 SUM_ADC1 的三字节电压64次采样累加和做平均数,并转化成相应量程数值
// Voltage = SUM_ADC1 / 64 *(10000/4096) = SUM_ADC1 * 625 / 256 / 64
// = SUM_ADC1 * 625 * 4 / 256 / 256
// (MAX = 27 10 00 00) = SUM_ADC1 * 125 * 5 * 4 / 256 / 256
// 舍弃后16位,2710 = 10000mV
//-------------------------------------
MOV BUF0_H , SUM_ADC1_H
MOV BUF0_M , SUM_ADC1_M
MOV BUF0_L , SUM_ADC1_L
MOV BUF1_L , #5D // 先乘以5 ,这个数比较小,不会导致BUF0_T的溢出
LCALL LONG_MUL_BYTE // SUM_ADC1 * 5
MOV BUF1_L , #125D
LCALL LONG_MUL_BYTE // SUM_ADC1 * 125 * 5
LCALL LONG_RL1
LCALL LONG_RL1 // SUM_ADC1 * 125 * 5 * 4
MOV VoltageH , BUF0_T
MOV VoltageL , BUF0_H // SUM_ADC1 * 125 * 5 * 4 / 256 / 256
RET
//-------------------------------------
Convert_Power:
// ----------- BUF0 = CurrentH*VoltageH , 结果放在BUF0(最大 20 000 000 ,01 31 2D 00H)
//------转换到量程范围,20.00 W ,需要除10000(27 10H), 分两次操作,每次除100.
MOV BUF0_L , CurrentL
MOV BUF0_H , CurrentH
MOV BUF1_L , VoltageL
MOV BUF1_H , VoltageH
LCALL WORD_MUL_WORD // BUF0 = (BUF0_H,BUF0_L)*(BUF1_H,BUF1_L)
//----- 除1000000,转化为相应的量程 ---------------------
MOV BUF1_L , #100D
LCALL LONG_DIV_BYTE
MOV BUF1_L , #10D // 现在是 mW为单位
LCALL LONG_DIV_BYTE
// Convert_Capacity: 计算累计放电电量
// ----------- Capacity += (PowerH,PowerL)
// ----------- MAX(PowerH,PowerL) = MAX_V*MAX_A = 10*2 = 20W = 2000D = 7 D0
// ----------- MAX(Capacity) = 7D0 * 86400秒 = A 4C B8 00
// ----------- 4字节Capacity逻辑上可以计量最大功率(20W)不少于24天.
MOV A , BUF0_L
ADD A , Capacity_L
MOV Capacity_L , A
MOV A , BUF0_M
ADDC A , Capacity_M
MOV Capacity_M , A
CLR A
ADDC A , Capacity_H
MOV Capacity_H , A
CLR A
ADDC A , Capacity_T
MOV Capacity_T , A
// 加上0.5各单位,也就是5 , 实现四舍五入.
MOV A , #005H
ADD A , BUF0_L
MOV BUF0_L , A
CLR A
ADDC A , BUF0_M
MOV BUF0_M , A
MOV BUF1_L , #10D // 除这个10 ,只是为了显示方便
LCALL LONG_DIV_BYTE // POWER / 100 /100 >> xx.xx W
MOV PowerH , BUF0_M
MOV PowerL , BUF0_L
RET
//-------------------------------------
Convert_Time:
INC Time_Sec
MOV A , Time_Sec
CJNE A , #060D , Exit_Convert_Time
MOV Time_Sec , #000H
INC Time_Min
MOV A , Time_Min
CJNE A , #060D , Exit_Convert_Time
MOV Time_Min , #000H
INC Time_Hor
MOV A , Time_Hor
CJNE A , #024D , Exit_Convert_Time
MOV Time_Min , #000H
INC Time_Day // 这个就不判断了,听天由命吧。
Exit_Convert_Time:
RET
/*****************************************************************************/
// ----------- 最后结果,以ASCII格式存在BUF0缓冲区,高位在BUF0_T 低位在BUF0_T
// ----------- 字符串不含小数点,所有字节都有效,‘0’不做消隐处理
// Ascii = BUF0_T,BUF0_H,BUF0_M,BUF0_L
RSEG ?PR?Current_To_Ascii?FUN_ADC
Current_To_Ascii:
MOV BUF0_T , CurrentH
MOV BUF0_H , CurrentL
LJMP WORD_To_ASCII
RSEG ?PR?Voltage_To_Ascii?FUN_ADC
Voltage_To_Ascii:
MOV BUF0_T , VoltageH
MOV BUF0_H , VoltageL
LJMP WORD_To_ASCII
RSEG ?PR?Power_To_Ascii?FUN_ADC
Power_To_Ascii:
MOV BUF0_T , PowerH
MOV BUF0_H , PowerL
LJMP WORD_To_ASCII
// ----------- 被除数在BUF0_T(msb),BUF0_H(lsb) , 除数在 BUF0_L
// ----------- 因为AD转换后,电流值最大 1999(07CFH) , 电压值最大 9999(270FH)
// ----------- 先除一个100 ,得到的商和余数均是单字节值,再做转换可直接使用硬件指令
// ----------- 先除以100 ,之后是2个单字节除法。
WORD_To_ASCII: MOV BUF0_L , #100D
LCALL WORD_DIV_BYTE // 除100后,结果,商在BUF0_H 余数在BUF0_L
LCALL WORD_To_BCD
RET
//-------------------------------------
RSEG ?PR?Capacity_To_LCD?FUN_ADC
/*电流采样分度5mA,对应5V功率为25mW,每秒可以累加的能量最小分度25mW秒/3600秒 = 0.007mWh。
实时功率累计值能显示mWh的最小单位应保持小数点后3位。
最大值参考最大功率10V*2A = 20W ,按照2天48小时计算,最大能量960Wh .
使用4字节进行计数,最大功率20W可以计时59小时。
0X100000000 / 0X4E20(20W)/3600(秒) = 59.65小时
最后采用格式 XXX.XXXXX Wh 比较稳妥。
XXXXXX.XXmWh
*/
Capacity_To_LCD:
MOV PCF8820_Y , #010D
MOV BUF0_T , Capacity_T
MOV BUF0_H , Capacity_H
MOV BUF0_M , Capacity_M
MOV BUF0_L , Capacity_L
MOV BUF1_L , #036D
LCALL LONG_DIV_BYTE
// ---------------------
//---- 调整量程,应该除以3600,但是显示单位其实是mWh
//---- 又因为保留小数点后5位,故除以36
MOV BUF1_L , #010D
LCALL LONG_DIV_BYTE
MOV A , BUF1_L
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #072D
LCALL PCF8820_Char8X16
// ---------------------
MOV BUF1_L , #010D
LCALL LONG_DIV_BYTE
MOV A , BUF1_L
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #064D
LCALL PCF8820_Char8X16
//-------------------------------
MOV BUF1_L , #010D
LCALL LONG_DIV_BYTE
MOV A , BUF1_L
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #056D
LCALL PCF8820_Char8X16
//-------------------------------
MOV BUF1_L , #010D
LCALL LONG_DIV_BYTE
MOV A , BUF1_L
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #048D
LCALL PCF8820_Char8X16
//-------------------------------
MOV BUF1_L , #010D
LCALL LONG_DIV_BYTE
MOV A , BUF1_L
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #040D
LCALL PCF8820_Char8X16
//-------------------------------
MOV BUF1_L , #010D
LCALL LONG_DIV_BYTE
MOV A , BUF1_L
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #024D
LCALL PCF8820_Char8X16
//-------------------------------
MOV BUF1_L , #010D
LCALL LONG_DIV_BYTE
MOV A , BUF1_L
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #016D
LCALL PCF8820_Char8X16
//-------------------------------
MOV BUF1_L , #010D
LCALL LONG_DIV_BYTE
MOV A , BUF1_L
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #008D
LCALL PCF8820_Char8X16
EXIT_Cap_To_Asc:
RET
//-------------------------------------
RSEG ?PR?Time_To_LCD?FUN_ADC
Time_To_LCD: MOV PCF8820_Y , #013D
MOV A , Time_Day
MOV B , #010D
DIV AB
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #008D
LCALL PCF8820_Char8X16
MOV A , B
ORL A , #'0'
MOV PCF8820_char, A
// MOV PCF8820_X , #016D
LCALL PCF8820_Char8X16
MOV A , Time_Hor
MOV B , #010D
DIV AB
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #032D
LCALL PCF8820_Char8X16
MOV A , B
ORL A , #'0'
MOV PCF8820_char, A
// MOV PCF8820_X , #016D
LCALL PCF8820_Char8X16
MOV A , Time_Min
MOV B , #010D
DIV AB
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #056D
LCALL PCF8820_Char8X16
MOV A , B
ORL A , #'0'
MOV PCF8820_char, A
// MOV PCF8820_X , #016D
LCALL PCF8820_Char8X16
MOV A , Time_Sec
MOV B , #010D
DIV AB
ORL A , #'0'
MOV PCF8820_char, A
MOV PCF8820_X , #080D
LCALL PCF8820_Char8X16
MOV A , B
ORL A , #'0'
MOV PCF8820_char, A
// MOV PCF8820_X , #016D
LCALL PCF8820_Char8X16
RET
/*****************************************************************************/
SUB_CLR_SUM: CLR A
MOV SUM_ADC0_H , A
MOV SUM_ADC0_M , A
MOV SUM_ADC0_L , A
MOV SUM_ADC1_H , A
MOV SUM_ADC1_M , A
MOV SUM_ADC1_L , A
RET
/*****************************************************************************/
END
C语言参数调用的寄存器分派规则,
可参照编写汇编的子函数,
用来被C语言的主函数调用。
; //-------------------------------------
?PR?_SUB1?SUB SEGMENT CODE
; //-------------------------------------
PUBLIC _SUB1
; //-------------------------------------
RSEG ?PR?_SUB1?SUB
_SUB1:
USING 0
RET
; END OF _SUB
; // ------低地址是高字节 低地址是高字节 低地址是高字节
; //-------------------------------------
; void SUB(R7)
; void SUB(R7 , R5)
; void SUB(R7 , R5 , R3 )
; //-------------------------------------
; void SUB(R6/R7)
; void SUB(R6/R7 , R4/R5 )
; void SUB(R6/R7 , R4/R5 , R2/R3 )
; //-------------------------------------
; void SUB(unsigned char i , unsigned char * cMATRIX)
;---- Variable 'ptr' assigned to Register 'R1/R2/R3' ----
;---- Variable 'i' assigned to Register 'R7' ----
MOV R3,#00H
MOV R2,#HIGH (cMATRIX)
MOV R1,#LOW (cMATRIX)
; //-------------------------------------
; unsigned char SUBr2(unsigned char cCHAR1 , unsigned char cCHAR2 )
MOV R7,A
RET
; //-------------------------------------
; unsigned int SUBr5(unsigned int iINT1 , unsigned int iINT2 )
MOV R6,iINT
MOV R7,iINT+01H
RET
|
|