注册 登录
电子技术论坛 返回首页

戴上举的个人空间https://bbs.elecfans.com/?703024[收藏][复制][分享][RSS]

博客

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

热度21已有 4554 次阅读2013-1-25 11:18

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

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

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

  例程为利用应广单片机的特点,用软件实现理论速率可以达到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

  }

  代码已编译,未调试

返回顶部