应广单片机系列——高速I2C接口

描述

经过一段时间的慎重考虑,在诸多朋友的支持下,决定在接下来的日子里,会尽可能多的写一些关于应广多核单片机应用的文章,希望能给有兴趣学习了解应广单片机的朋友提供到点滴帮助。

      这个针对应广双核、多核单片机应用的系列,会以具体程序为例,在程序中加以注释,只要条件允许,例程都会经过调试,如果只是编译没有调试的,我会注明。

      附件中的例程代码,读者可以自由使用,不需要通知我,如有可能,希望在代码中保留我的签名信息,深表感谢!

      例程为利用应广单片机的特点,用软件实现理论速率可以达到1M的I2C通讯接口,如果是其它普通单片机,也可以用软件模拟出高速I2C,不同点是应广实现模拟后还能够实现各种控制功能,而其它普通单片则不能。


//-----------------------------------------
//应广单片机软件实现高速I2C接口例程(SALVE模式)
//本例仅供参考,欢迎指正程序中的问题
//本例利用应广单片机的双核特点
//用一个核专门对I2C接口的IO进行扫描等待
//对I2C接口的高低变化利用应广特有的IO状态等待指令高速实现IO口跳变判断
//利用定时器进行超时判断
//理论上可以让模拟的I2C接口达到1M的速率
//2012年12月15日
//
//作者:戴上举
//邮箱:daishangju@163.com
//博客:forum.eet-cn.com/BLOG_daishangju_334.HTM
//电话:13509678051
//Q  Q:1514292225
//-----------------------------------------

.chip p201cs14a
//{{PADAUK_CODE_OPTION
 .Code_Option Bootup  Slow  // 1024 ILRC
 .Code_Option LVD  2.79V  // Maximum performance = 4 MIPS
 .Code_Option Security Enable  // Security 3/4 words Enable
//}}PADAUK_CODE_OPTION

//定义I2C接口要用的IO口,用户可以自己修改这里的IO口定义
I2C_SDA equ pa.0
I2C_SDA_LOW equ set0 I2C_SDA
I2C_SDA_HIGH equ set1 I2C_SDA
I2C_SDA_INPUT equ set0 pac.0
I2C_SDA_OUTPUT equ set1 pac.0
I2C_SCL equ pa.4
//定义I2C设备地址,用户可以自己修改此地址
I2C_READ_CMD equ 0x7F
I2C_WRITE_CMD equ 0x7E


word init_timer

//byte Xms
//byte ms_cnt

byte i2c_device //用来存放I2C接口地址
byte i2c_write_byte //I2C进行写操作时存放I2C写入的数据
byte i2c_read_byte //I2C进行读操作时候读出的内容

bit i2c_start_flag

//应广单片机程序入口,第一条必须为跳转到第一个内核主程序入口地址的指令,第二条为第二个内核,有几个内核就有几条
.romadr 0x000
 goto main0
 goto main1


//应广单片机中断程序入口地址,所有中断共用同一个入口,需要用户自己判断中断类型
.romadr 0x010
 pushaf //压栈
 if(intrq.T16) //判断是否为定时中断
 {
  stt16 init_timer //清内部TIMER计数器
  if(i2c_start_flag) //启动I2C通讯处理后这个标志会被置1
  {
   I2C_SDA_INPUT
   reset //系统复位
  }
 }
 intrq = 0 //清中断标志
 popaf //弹栈
 reti //中断返回

//----------------------------------------
//input: ms
//用该函数可以再4M的频率下得到近似1毫秒的延时,在第一个内核中调用中断会导致延时加长
//----------------------------------------
/*delayXms:
 while(Xms)
 {
  wdreset
  ms_cnt = 20
  while(ms_cnt)
  {
   delay 195
   ms_cnt--
  }
  Xms--
 }
 ret*/

//用IO口模拟I2C slave模式的子函数
i2c_slave:
 I2C_SDA_INPUT //将SDA设为输入
i2c_start:
 //I2C空闲状态下SDA和SCL同为高电平,要启动I2C前初始状态必须是两者同为高
 stt16 init_timer //清内部TIMER计数器
 if(!I2C_SCL) //如果SCL为低,此时不用启动I2C通讯处理
 {
  goto i2c_stop
 }
 if(!I2C_SDA) //如果SDA为低,此时不用启动I2C通讯处理
 {
  goto i2c_stop
 }

 i2c_start_flag = 1 //启动I2C通讯处理,这个标志位会在定时中断中用到

 //I2C的START信号是SDA和SCL同为高电平装态下SDA先变为低,然后SCL变为低
 //判断I2C START信号
 //等待SDA从高变低
 stt16 init_timer //清内部TIMER计数器
 wait0 I2C_SDA  //应广特有的等待IO变低指令,等待SDA从高变低,如果长时间没有变低,会触发定时中断,系统复位
 nop //加适当延时消除IO抖动的影响
 nop
 nop
 nop
 //等待SCL从高变低,原理同上
 stt16 init_timer
 wait0 I2C_SCL  //if overtime MCU will auto reset
 nop
 nop
 nop
 nop

 //已经判断为得到有效START信号


 //开始接收I2C的器件地址,为了实现高速处理,程序顺序处理,没有使用循环处理方式
 i2c_device = 0
 stt16 init_timer  //device bit7
 wait1 I2C_SCL
 nop
 nop
 nop
 nop
 if(I2C_SDA)
 {
  set1 i2c_device.7
 }
 wait0 I2C_SCL
 nop
 nop
 nop
 nop
 stt16 init_timer  //device bit6
 wait1 I2C_SCL
 nop
 nop
 nop
 nop
 if(I2C_SDA)
 {
  set1 i2c_device.6
 }
 wait0 I2C_SCL
 nop
 nop
 nop
 nop
 stt16 init_timer  //device bit5
 wait1 I2C_SCL
 nop
 nop
 nop
 nop
 if(I2C_SDA)
 {
  set1 i2c_device.5
 }
 wait0 I2C_SCL
 nop
 nop
 nop
 nop
 stt16 init_timer  //device bit4
 wait1 I2C_SCL
 nop
 nop
 nop
 nop
 if(I2C_SDA)
 {
  set1 i2c_device.4
 }
 wait0 I2C_SCL
 nop
 nop
 nop
 nop
 stt16 init_timer  //device bit3
 wait1 I2C_SCL
 nop
 nop
 nop
 nop
 if(I2C_SDA)
 {
  set1 i2c_device.3
 }
 wait0 I2C_SCL
 nop
 nop
 nop
 nop
 stt16 init_timer  //device bit2
 wait1 I2C_SCL
 nop
 nop
 nop
 nop
 if(I2C_SDA)
 {
  set1 i2c_device.2
 }
 wait0 I2C_SCL
 nop
 nop
 nop
 nop
 stt16 init_timer  //device bit1
 wait1 I2C_SCL
 nop
 nop
 nop
 nop
 if(I2C_SDA)
 {
  set1 i2c_device.1
 }
 wait0 I2C_SCL
 nop
 nop
 nop
 nop
 stt16 init_timer  //device bit0
 wait1 I2C_SCL
 nop
 nop
 nop
 nop
 if(I2C_SDA)
 {
  set1 i2c_device.0
 }
 wait0 I2C_SCL
 //nop //后面的比较操作会耗费时间,可以不用延时
 //nop
 //nop
 //nop


 if(i2c_device == I2C_READ_CMD) //I2C进行读操作,同样为了实现高速处理,程序顺序处理,没有使用循环处理方式
 {
  //回复slave ACK信号
  I2C_SDA_OUTPUT
  I2C_SDA_LOW
  stt16 init_timer
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
 

  stt16 init_timer //i2c_read_byte bit7
  if(i2c_read_byte.7)
  {
   I2C_SDA_HIGH
  }
  else
  {
   I2C_SDA_LOW
  }
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_read_byte bit6
  if(i2c_read_byte.6)
  {
   I2C_SDA_HIGH
  }
  else
  {
   I2C_SDA_LOW
  }
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_read_byte bit5
  if(i2c_read_byte.5)
  {
   I2C_SDA_HIGH
  }
  else
  {
   I2C_SDA_LOW
  }
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_read_byte bit4
  if(i2c_read_byte.4)
  {
   I2C_SDA_HIGH
  }
  else
  {
   I2C_SDA_LOW
  }
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_read_byte bit3
  if(i2c_read_byte.3)
  {
   I2C_SDA_HIGH
  }
  else
  {
   I2C_SDA_LOW
  }
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_read_byte bit2
  if(i2c_read_byte.2)
  {
   I2C_SDA_HIGH
  }
  else
  {
   I2C_SDA_LOW
  }
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_read_byte bit1
  if(i2c_read_byte.1)
  {
   I2C_SDA_HIGH
  }
  else
  {
   I2C_SDA_LOW
  }
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_read_byte bit0
  if(i2c_read_byte.0)
  {
   I2C_SDA_HIGH
  }
  else
  {
   I2C_SDA_LOW
  }
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop

  I2C_SDA_INPUT
  stt16 init_timer //master ack/nack
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  nop
  nop
  nop
  nop
  stt16 init_timer //END
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  stt16 init_timer
  wait1 I2C_SDA
  nop
  nop
  nop
  nop
 }
 else if(i2c_device == I2C_WRITE_CMD) //I2C是进行写操作,同样为了实现高速处理,程序顺序处理,没有使用循环处理方式
 {
  //slave ACK
  I2C_SDA_OUTPUT //--------I2C SDA input/output switch----------
  I2C_SDA_LOW
  stt16 init_timer
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop

  I2C_SDA_INPUT
  i2c_write_byte = 0
  stt16 init_timer //i2c_write_byte bit7
  if(I2C_SDA)
  {
   set1 i2c_write_byte.7
  }
  nop
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_write_byte bit6
  if(I2C_SDA)
  {
   set1 i2c_write_byte.6
  }
  nop
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_write_byte bit5
  if(I2C_SDA)
  {
   set1 i2c_write_byte.5
  }
  nop
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_write_byte bit4
  if(I2C_SDA)
  {
   set1 i2c_write_byte.4
  }
  nop
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_write_byte bit3
  if(I2C_SDA)
  {
   set1 i2c_write_byte.3
  }
  nop
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_write_byte bit2
  if(I2C_SDA)
  {
   set1 i2c_write_byte.2
  }
  nop
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_write_byte bit1
  if(I2C_SDA)
  {
   set1 i2c_write_byte.1
  }
  nop
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  stt16 init_timer //i2c_write_byte bit0
  if(I2C_SDA)
  {
   set1 i2c_write_byte.0
  }
  nop
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop

  //slave NACK
  I2C_SDA_OUTPUT //--------I2C SDA input/output switch----------
  I2C_SDA_HIGH
  stt16 init_timer
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  wait0 I2C_SCL
  //nop
  //nop
  //nop
  //nop
  I2C_SDA_INPUT //--------I2C SDA input/output switch----------
  nop
  nop
  stt16 init_timer //END
  wait1 I2C_SCL
  nop
  nop
  nop
  nop
  stt16 init_timer
  wait1 I2C_SDA
  //nop
  //nop
  //nop
  //nop

  //下面代码用户可根据实际情况进行修改,这里是将I2C写入的数据取反后放到读操作位置
  i2c_read_byte = ~i2c_write_byte
 
 }

i2c_stop:
 I2C_SDA_INPUT
 i2c_start_flag = 0 //I2C stop work

 ret

//----------------FPPA0-------------------
main0:


 .ADJUST_OTP_IHRCR 8MIPS  // IHRC/2 = 8MIPS, WatchDog Disable, RAM 0,1 temporary be used
 sp = 0x30

 disgint
 inten = 0

 mov a,0b000_11_111 //disable timer
 mov t16m,a

 delay 200

 clkmd.0 = 0 //pa.5 as GPIO

 //注意IO口的输入输出设定
 pa = 0b1111_1111
 pac = 0b0000_0000
 paph = 0b1111_1111
 pb = 0b1111_1111
 pbc = 0b0000_0000
 pbph = 0b1111_1111


 init_timer = 7768 //从7768进行校准为100ms
 mov a,0b100_11_111
 mov t16m,a
 stt16 init_timer


 delay 200

 mov a,0
 mov intrq,a

 i2c_start_flag = 0 //I2C not start work


// adcdi = 0b0000_0100 //pb2 is analog input
// adcc = 0b10_0010_00 //enable ADC, select pb2
// adcm = 0b000_0100_0 //system clock/16
 //adcm = 0b000_0111_0 //system clock/128
 set1 fppen.1 //eanble FPPA1
 clkmd.1 = 1 //enable watch dog
 wdreset //clear watch dog

// Xms = 100
// call delayXms

 stt16 init_timer
 intrq = 0
 inten.T16 = 1 //打开定时中断
 engint


main0_loop:
 init_timer = 0
 wdreset
 //用户可以在这里添加自己想要的任意代码,这里可以实现任意一个普通单片机能够是想的功能
 goto main0_loop


//----------------FPPA1-------------------
main1:
 sp = 0x38
main1_loop:
 call i2c_slave
 goto main1_loop

程序中的多个NOP可以用DELAY 3代替,用DELAY 指令可以节省程序空间
 

 I2C启动判断代码用下面部分更可靠(2012.12.22)

//wait SDA and SCL high at the same time
 while((!I2C_SDA) || (!I2C_SCL))
 {
  stt16 init_timer
  nop
  nop
  nop
  nop
 }


 if(!I2C_SDA)
 {
  goto i2c_stop
 }
 if(!I2C_SCL)
 {
  goto i2c_stop
 }

 

代码已编译,未调试


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分