基于MSP430单片机的虚拟I2C总线软件包VIIC_M1.0
一、VIIC_M1.0软件包的组成
根据归一化设计的要求,主方式下虚拟I2CC总线由下列10个子程序组成:
1、时序模拟子程序4个
I2C_sta,I2C _stop,I2C _mack ,I2C _mnack
2、操作模拟子程序3个
I2C_ackn,I2C _wr_byte,I2C _rd_byte
3、数据读写子程序3个
I2C_wr_nbyte,I2C _rd_nbyte,I2C_wr_addr
二、应用对象
1、MSP430单片机
MSP430单片机为低功耗的16位单片机,有MSP430X11、MSP430X112、MSP430X1101、MSP430X13X、MSP430X14X、MSP430X31X/32X/33X、MSP430X41X等型号,每种芯片都有丰富的I/O端口。本设计采用芯片为MSP430E325
(1)虚拟I2C总线所使用的I/O端口
>数据线(SDA)使用的是通用端口P0中的P0.7,该端口为输入/输出双向口,有输入寄存器(P0IN)、输出寄存器(P0OUT)及方向寄存器(P0DIR)等寄存器,通过字节指令访问。>>时钟线(SCL)使用的是通用定时器/端口TP中的TP.1,该端口为输出口,有定时器/端口控制寄存器(TPCTL)、定时器/端口数据寄存器(TPD)及定时器/端口允许寄存器(TPE)等,通过字节指令访问。
(2)运行时所使用的时钟频率
MSP430E325运行时用两个时钟:辅助时钟(ACLK)和主时钟(MCLK)。当采用32768KHz的晶体振荡器,并且系统时钟控制寄存器采用缺省值时,主时钟的频率为1.049MHz。若主时钟的频率不是1.049MHz,请适当调整程序中nop的个数。
2、EEPROM器件
(1)EEPROM器件
本例使用的EEPROM器件为24LC65(MICROCHIP)。24LC65容量为8KB,其中的字节地址为13位,分两个字节:
SUBADDR_H为高5位地址 SUBADDR_L为低8位地址;24LC65的封装引脚如图1所示:
(2)24LC65的数据格式
A、当前地址写:S,SLA+W,A,DATA1,A,DTA2,A...,DATAn,A,P
B、当前地址读:S,SLA+R,A,DATA1,A,DTA2,A...,DATAn,/A,P
C、指定地址写:S,SLA+W,A,subaddr_H,A,subaddr_L,A,DATA1,A,DATA2,A,...,DATAn,A,P
D、指定地址读:S,SLA+R,A,subaddr_H,A,subaddr_L,A,DATA1,A,DATA2,A,...,DATAn,/A,P
其中:SLA+W,SLA+R,subaddr_H,subaddr_L为主控器件发出的数据
S,A,/A,P为主控器件发出的信息
DATA1,DATA2,...,DATAn为被控器件发给主控器件的数据
A 为被控器件发给主控器件的信息
(3)24LC65与MSP430-325的连接如图2所示
三、应用界面
1、发送N字节数据——从当前地址开始
;发送的数据在MTD中
;
;数列格式:S,SLA+W,subaddr_H,A,subaddr_L,A,DATA1,A,DATA2,A...,DATAn,A,P
;MOV.b #CODE,SLA;I2C_R_R/W=0;A0=A1=A2=0
MOV.b #N,Num_byt;发送字节数
CALL I2C_WR_Nbyte;从当前地址开始写
2、接收N字节数据——从指定地址开始读
;接收的数据在MRD中
;
;数列格式:S,SLA+W,A,subaddr_H,A,subaddr_L,A,
S,SLA+R,A,DATA1,A,DATA2,A,...,DATAn,/A,P
MOV.b #CODE+SLAR/W,SLA
;SLAR/W=1,A0=A1=A2=0
MOV.b #N,Num_byt ;接收字节数
CALL #I2C_RD_Nbyte ;从当前地址开始读
3、接收N字节——从指定地址开始读
;接收的数据存放在MRD中
;
;数列格式:S,SLA+W,A,subaddr_H,A,subaddr_L,A,
;S,SLA+R,A,DATA1,A,DATA2,A,...,DATAn,/A,P
;
MOV.b #CODE,SLA
;SLAR/W=0:写,A0=A1=A2=0
MOV.b #N,Num_byt ;接收字节数
MOV #Subaddr,I2C_R_Addr ;字节地址送入暂存单元
CALL #I2C_R_Addr ;发送字节地址
MOV.b #CODE+SLAR/W,SLA;SLAR/W=1:读,A0=A1=A2=0
CALL #I2C_RD_Nbyte
4、发送N字节——从指定地址开始写
;数列格式:S,SLA+W,A,Subaddr_H,A,subaddr_L,A,DATA1,A,DATA2,A,...,DATAn,A,P
;
MOV.b #CODE,SLA;SLAR/W=0:写,A0=A1=A2=0
MOV #N,Num_byt ;发送字节数
MOV #Subaddr,I2C_R_Addr ;字节地址送入暂存单元
CALL #I2C_WR_Addr ;发送字节地址
CALL #WR_Nbyte
后记:
1、如果使用MSP430中别的I/O端口,只需要在模拟时序子程序中改动相应的积存器即可。本例中时钟线使用TP口,是因为它为输出口,在程序中可以节省指令。
对于24系列的其他芯片,由于容量不同,请特别注意其字节地址是1个字节还是2个字节。本例的24LC65字节地址就是2个字节的。
2、MSP430系列的单片机本人是初学使用,程序虽经初步调试通过,轻易抛出,主意在为引玉之砖,作为学习的一点体会。缺点和错误在所难免,望各位不吝指教。
VIIC_M1.0软件包
⑴ MSP430各寄存器的定义:
;*************************************
;定义TP0.0--TP0.5各端口的各种控制寄存器
;*************************************
;
TPD .set 004EH ;TP端口数据寄存器
TPE .set 004FH ;TP端口输出允许寄存器
TP.0 .set 01H
TP.1 .set 02H
TP.2 .set 04H
TP.3 .set 08H
TP.4 .set 10H
TP.5 .set 20H
;*************************************************
;定义P0.0--P0.7各端口的各种控制寄存器
;*************************************************
;
P0IN .set 0010H ;P0端口输入寄存器
P0OUT .set 0011H ;P0端口输出寄存器
P0DIR .set 0012H ;P0端口方向寄存器
P0.0 .set 01H
P0.1 .set 02H
P0.2 .set 04H
P0.3 .set 08H
P0.4 .set 10H
P0.5 .set 20H
P0.6 .set 40H
P0.7 .set 80H
;
CODE .EQU 0A0H ;从器件编码
SLAR/W .EQU 1H ;读时为1,写时为0
A0 .EQU 2H ;器件地址位
A1 .EQU 4H ;器件地址位
A2 .EQU 8H ;器件地址位
VSDA .set 80H ;P0.7
VSCL .set 02H ;TP.1
SLA .EQU 200h ;寻址字节存放单元
Num_byt .EQU 201h ;发送数据或接收数据的个数
I2C_R_Addr .EQU 202h ;EEPROM字节地址存放单元
;202h为高5位,低8位
MTD .EQU 210h ;发送缓冲区
MRD .EQU 220h ;接收缓冲区
I2Cdata .EQU R7 ;数据暂存单元
Count .EQU R8 ;计数器
Mask .EQU R9 ;屏蔽字
Subaddr .EQU 0xxxxh ;EEPROM字节地址
;Subaddr_H:高5位,Subaddr_L:低8位
N .EQU 0yyh ;发送或接收的字节数
;
⑵ 子程序集
① 时序模拟子程序
;ⅰ.开始条件:启动总线
;
I2C_Sta bis.b #VSDA,&P0DIR ;设置VSDA为输出
bis.b #VSDA,&P0OUT ;VSDA输出为高
bis.b #VSCL,&TPE ;VSCL输出允许
bis.b #VSCL,&TPD ;VSCL输出为高
nop
nop
bic.b #VSDA,&P0OUT ;VSDA输出为低
nop
nop
bic.b #VSCL,&TPD ;VSCL输出为低
ret
;
;ⅱ.停止条件:停止数据传送
;
I2C_Stop bis.b #VSDA,&P0DIR ;设置VSDA为输出
bic.b #VSDA,&P0OUT ;VSDA输出为低
bis.b #VSCL,&TPD ;VSCL输出为高
nop
nop
bis.b #VSDA,&P0OUT ;VSDA输出为高
nop
nop
bic.b #VSCL,&TPD ;VSCL输出为低
bic.b #VSCL,&TPD ;VSCL输出禁止
ret
;
;ⅲ.发送应答位
;
I2C_Mack bis.b #VSDA,&P0DIR ;设置VSDA为输出
bic.b #VSDA,&P0OUT ;VSDA输出为低
bis.b #VSCL,&TPD ;VSCL输出为高
nop
nop
bic.b #VSDA,&P0OUT ;VSDA输出为低
bic.b #VSCL,&TPD ;VSCL输出为低
bis.b #VSDA,&P0OUT ;VSDA输出为高
ret
;
;ⅳ.发送非应答位
;
I2C_MNack bis.b #VSDA,&P0DIR ;设置VSDA为输出
bis.b #VSDA,&P0OUT ;VSDA输出为高
bis.b #VSCL,&TPD ;VSCL输出为高
nop
nop
bic.b #VSCL,&TPD ;VSCL输出为低
bic.b #VSDA,&P0OUT ;VSDA输出为低
ret
② 操作模拟子程序
;
; ⅰ.检查应答位
; 应答位在进位位(Carry)中
;
I2C_Ackn bis.b #VSCL,&TPD ;VSCL输出为高,使VSDA上数据有效
nop
nop
bic.b #VSDA,&P0DIR ;设置VSDA为输入
bit.b #VSDA,&P0IN ;读数据进近位位(Carry)
;(Acknowledge bit)
nop
nop
bic.b #VSCL,&TPD ;VSCL输出为低
nop
ret
; ⅱ. 发送数据字节
;
;发送的数据在I2CData中
;占用Mask,I2CData
;
I2C_WR_BYT mov.b #80H,Mask ;位屏蔽:MSB 在先
I2C_WR_BYT1 bit.b Mask,I2CData ;信息位进进位位(Carry)
jc I2C_WR_BYT2
bis.b #VSDA,&P0DIR ;信息位是0
bic.b #VSDA,&P0OUT ;VSDA输出为低
bis.b #VSCL,&TPD ;VSCL输出为高
nop
nop
bic.b #VSCL,&TPD ;VSCL输出为低
jmp I2C_WR_BYT3
;
I2C_WR_BYT2 bis.b #VSDA,&P0DIR ;信息位是 1
bis.b #VSDA,&P0OUT ;VSDA输出为高
bis.b #VSCL,&TPD ;VSCL输出为高
nop
nop
bic.b #VSCL,&TPD ;VSCL输出为低
bic.b #VSDA,&P0OUT ;VSDA输出为低
;
I2C_WR_BYT3 clrc
rrc.b Mask ;指向下一位
jnc I2C_WR_BYT1 ;进位位为0,继续
nop
nop
ret
;
;ⅲ. 接收数据字节
;
;接收的数据在I2CData中
;占用I2CData,Count
;
I2C_RD_BYT clr I2CData
mov.b #8,Count ;设置接收数据字节的移位次数
;
I2C_RD_BYT1 bic.b #VSDA,&P0DIR ;设置VSDA为输入
bis.b #VSCL,&TPD ;VSCL输出为高,使VSDA上数据有效
nop
bit.b #VSDA,&P0IN ;读入的位在C中,是"1"或是"0"?
rlc.b I2CData ;从进位位开始左移
nop
nop
bic.b #VSCL,&TPD ;设置VSCL输出为低,可继续接收数据位
nop
nop
dec Count
jnz I2C_RD_BYT1 ;8位数据没有移完,则继续移位
ret
③ 数据读写子程序
;
;ⅰ.发送N字节数据
; --从当前地址开始发送
;
;数据在MTD中
;SLA中为SLAW
;字节数在Num_byt中
;占用R6
;调用:I2C_Sta,I2C_WR_BYT,I2C_Ackn,I2C_Stop
;数列格式: S,SLA+W,A,DATA1,A,DATA2,A,....,DATAn,A,P
;
I2C_WR_Nbyte
WR_Conrol_byte
CALL #I2C_Sta
mov.b SLA,I2CData ;送出SLAW
CALL #I2C_WR_BYT
CALL #I2C_Ackn
JC WR_Conrol_byte ;C=0,继续
WR_Nbyte
clr R6
WR_Nbyte1
MOV.B MTD(R6),I2CData
CALL #I2C_WR_BYT
CALL #I2C_Ackn
JC WR_Nbyte
inc R6
cmp.b Num_byt,R6
JNZ WR_Nbyte1
CALL #I2C_Stop
RET
;
;ⅱ. 接收N字节数据
; --从当前地址开始读
;
;数据存放在MRD中
;接收的字节数在Num_byt中
;数列格式: S,SLA+R,A,DATA1,A,DATA2,A,....,DATAn,/A,P
;
I2C_RD_Nbyte
RD_Conrol_byte
CALL #I2C_Sta
mov.b SLA,I2CData ;送出SLAR
CALL #I2C_WR_BYT
CALL #I2C_Ackn
JC RD_Conrol_byte ;检查应答位
RD_Nbyte
clr R6
RD_Nbyte1
CALL #I2C_RD_BYT
MOV.B I2CData,MRD(R6)
inc R6
cmp.b Num_byt,R6
JNZ RD_Nbyte2
CALL #I2C_MNack ;N字节数据读完,送出非应答位
CALL #I2C_Stop ;送出Stop
RET
RD_Nbyte2 CALL #I2C_Mack ;送出应答位
JMP RD_Nbyte1
;
;ⅲ. 向EEPROM发送数据单元地址
;
;EEPROM中字节地址在I2C_R_Addr(高),I2C_R_Addr+1(低)中
;数列格式:S,SLA+W,A,Subaddr_H,A,Subaddr_L,A
;
I2C_WR_Addr
WR_Addr CALL #I2C_Sta
mov.b SLA,I2CData ;送出SLAW
CALL #I2C_WR_BYT
CALL #I2C_Ackn
JC WR_Addr
MOV.b I2C_R_Addr,I2CData ;送出字节地址的高5位
CALL #I2C_WR_BYT
CALL #I2C_Ackn
JC WR_Addr
MOV.b I2C_R_Addr+1,I2Cdata ;送出字节地址的低8位
CALL #I2C_WR_BYT
CALL #I2C_Ackn
JC WR_Addr
RET
(北京工业大学 张俊谟)