当前位置:   article > 正文

Mini2440裸机开发之I2C(AT24C08)

at24c08写一个字节多长时间

通信协议-I2C小节,我们已经对I2C协议以及AT24C08芯片进行了详细的介绍,这里就不在重复赘述。

一、S3C2440上的I2C

1.1 I2C概述

I2C的使用位于S3C2440芯片手册的第20章。S3C2440可以支持一个多主控I2C总线串行接口。一条专用串行数据线(SDA)和一条专用串行时钟线(SCL)连接到I2C总线的总线主控和外设之间的信息。SDA和SCL线都为双向的。

为了控制多主控IIC总线操作,必须写入值到以下寄存器中:

  • 多主控IIC总线控制寄存器,IICCON;
  • 多主控IIC总线控制/状态寄存器,IICSTAT;
  • 多主控IIC总线Tx/Rx数据移位寄存器,IICDS;
  • 多主控IIC总线地址寄存器,IICADD;
1.2 I2C方块图

可以通过编程IICCON寄存器的4位预分频器值来控制串行时钟SCL的频率,I2C总线接口地址被存储在IICADD寄存器。

1.3 I2C总线接口

S3C2440的I2C总线接口有 4 种工作模式:

  • 主机发送模式;
  • 主机接收模式;
  • 从机发送模式;
  • 从机接收模式;

二、I2C相关寄存器

2.1 IIC总线控制寄存器IICCON
寄存器地址R/W描述复位值
IICCON0x54000000R/WIIC总线控制寄存器0x0X

寄存器位信息:

IICCON描述初始状态
应答发生[7]

IIC总线应答使能位

0:禁止       1:允许

对于发送模式。不需要配置ack应答信号

对于接收模式,设置为1,让它在第9个CLK发出ack应答信号

0
TX时钟源选择[6]

SCL时钟源选择位

0:IICCLK = PCLK/16    1:IICCLK=PCLK/512

0
TX/RX中断[5]

IIC总线TX/RX中断使能/禁止位

0:禁止      1:允许

0
中断挂起标志[4]

IIC总线TX/RX中断标志位。

当此位为1时,SCL被拉低,IIC传输停止;

如果要继续传输,需要写入0清除它;

当读出此位为0时,表示没有中断发生,当读出此位为1时表示有中断发生;

0
发送时钟值[3:0]

IIC总线发送时钟预分频系数:

SCL = IICCLK/(IICCON[3:0]+1)

未定义
 2.2 IIC总线控制/状态寄存器IICSTAT
寄存器地址R/W描述复位值
IICSTAT0x54000004R/WIIC总线控制/状态寄存器0x0

寄存器位信息:

IICSTAT描述初始状态
模式选择[7:6]

模式选择

00:从接收模式

01:从发送模式

10:主接收模式

11:主发送模式

00
忙信号状态/起始停止条件[5]

IIC总线忙信号状态位

当读的时候,0表示not busy,1表示busy

当写的时候,0表示写入STOP,1表示写入START

0
串行输出[4]

IIC总线数据输出使能/禁止:

0:禁止    1:使能

00
仲裁状态标志位[3]

IIC总线仲裁过程状态标志位

0:总线仲裁成功    1:串行I/O间总线仲裁失败

0
从地址状态标志[2]

IIC总线从地址状态标志位

0:发现起始/停止条件清除  1:接收到从地址与IICADD中地址值匹配

0
地址零状态标志[1]

 IIC总线地址零状态标志位

0:发现起始/停止条件清除    1:接收从地址为0x000000b

 0
最后收到位状态标志[0]

IIC总线最后收到位状态标志

表示I2C总线上的第9个时钟周期有没有ack,0表示有ack, 1表示无ack

 0
2.3 IIC总线地址寄存器IICADD
寄存器地址R/W描述复位值
IICADD0x54000008R/WIIC总线地址寄存器0x0XX

寄存器位信息:

IICADD描述初始状态
从设备地址[7:0]

从设备7位地址

当IICSTAT中串行输出使能=0时,IICADD为写使能;

从地址:[7:1]

未映射:[0]

XXXXXXXX
2.4 IIC总线发送/接收以为寄存器IICDS
寄存器地址R/W描述复位值
IICDS0x5400000CR/WIIC总线发送/接收移位寄存器0x0XX

寄存器位信息:

IICDS描述初始状态
从设备地址[7:0]

IIC总线TX/RX操作的8位数据移位寄存器

当IICSTAT中串行输出使能=0时,IICADS为写使能;

XXXXXXXX

三、读写操作流程 

3.1. 主机发送模式

其流程如下:

  1. 设置IICCON寄存器;;
    • 允许应答,位[7]=1;
    • 使能中断,位[5]=1;
    • 定义SCL周期,位[6]、[3:0];
  2. 设置IICSTAT位[4]使能串行输出;
  3. 写从设备的地址到IIICDS寄存器;
  4. 写0xF0到IICSTAT发送IICDS中的数据;
    • 使能串行输出,位[4]=1;
    • 配置主机为TX模式,位[7:6]=11;
    • 主设备开始发送START信号;
  5. IICDS中配置的数据(从设备地址7位 + 读写1位位)被发送出去,每传输完一个字节数据将产生一个中断;
  6. 清除IICCON挂起位位[4];
  7. 通过查询IICSTAT位[0]判断是否接收到从设备的ACK应答;
  • 如果没有接收到从设备ACK应答:写0xD0到IICSTAT,写0xAF到ICCCON,主设备发送停止信号,等待一会等待停止信号生效;
  • 如果接收到从设备ACK应答:继续向IICDS写入要发送的数据,数据被发送出去,继续IIC数据传输;
3.2 主机接收模式

其流程如下:

  1. 设置IICCON寄存器;
    • 允许应答,位[7]=1;
    • 使能中断,位[5]=1;
    • 定义SCL周期,位[6]、[3:0];
  2. 设置IICSTAT位4使能串行输出;
  3. 写从设备的地址到IIICDS寄存器;
  4. 写0xB0到IICSTAT发送IICDS中的数据;
    • 使能串行输出,位[4]设置为1;
    • 配置主机为RX模式,位[7:6]设置为10;
    • 主设备开始发送START信号;
  5. IICDS中配置的数据(从设备地址7位 + 读写1位位)被发送出去,每传输完一个字节数据将产生一个中断;
  6. 清除IICCON挂起位位[4];
  7. 通过查询IICSTAT位[0]判断是否接收到从设备的ACK应答;
    • 如果没有接收到从设备ACK应答:写0x90到IICSTAT,主设备发送停止信号,等待一会等待停止信号生效;
    • 如果接收到从设备ACK应答:从IICDS读取数据,并清除IICCON挂起位,数据被接收到,主设备发送应答信号,继续IIC数据读取;

四、代码

4.1 硬件接线

Mini2440开发板外接了一个I2C信号引脚的EEPROM芯片AT24C08,它有1024字节,供用户测试I2C总线。

这里I2CSCL连接的S3C2440引脚GPE14、I2CSCL连接的S3C2440引脚GPE15。

4.2 I2C初始化
4.2.1 IO状态设置

设置GPE14、GPE15功能复用为I2C。设置GPE控制寄存器,同时禁止GPE14、GPE15为上拉使能。

 

  1. GPECON &= ~((3<<28) | (3 <<30)); /* 清零 */
  2. GPECON |= ((2<<28) | (2<<30)); /* 设置为IIC */
  3. GPEUP |= 0xc000; /* 禁止内部上拉 */
4.2.2 设置TX/RX中断、SCL周期
  1. /* 允许应答、使能TX/RX中断、SCL周期 */
  2. IICCON |= (1<<7 | 0<<6 | 1<<5 | 0xf);
  3. IICSTAT |= (1<<4); /* 使能TX/RC */

允许应答。PLCK为50MHz,IICCLK=  PCLK/`16,允许中断,发送时钟 = IICLK/16。

SCL时钟频率为:$int(\frac{50000000}{16*(15+1)})=1953125=0.198MHZ$

4.2.3 清除SRCPND、INTPND,取消中断屏蔽
  1. SRCPND |= BIT_IIC; /* 向相应位置写1清除源挂起寄存器 */
  2. INTPND |= BIT_IIC; /* 向相应位置写1清除挂起寄存器 */
  3. INTMSK &= ~BIT_IIC; /* 关闭UART0中断屏蔽,总中断 */
4.3 向AT24C08写入n个字节数据

参考主机发送模式流程图编写代码,需要注意的是代码和流程图有部分出入,以下代码测试是可以运行的:

  1. /*************************************************************
  2. *
  3. * Function : 主设备发送一个字节数据后,等待从设备应答
  4. * Return : 0 有应答 1无应答
  5. *
  6. **************************************************************/
  7. u8 iic_wait_ack()
  8. {
  9. IICCON &= ~(1<<4); /* 清除IICCON挂起位,必须先清除 */
  10. /* 数据发送完成,会进入中断函数,设置标志位;一旦进入IIC中断,即可跳出该死循环 */
  11. while(iic_isr_flag == 0);
  12. /* 等待从设备应答信号 */
  13. delay_us(10);
  14. /* 没有应答,发送停止信息 */
  15. if(IICSTAT & 0x01 == 1){
  16. IICSTAT = 0xD0; /* 主设备发送停止信号 */
  17. IICCON = 0xAF; /* 禁止TX/RX中断,为下次通讯做准备 */
  18. delay_us(10); /* 等待一会等待停止信号生效 */
  19. printf("tx err, no ack\r\n");
  20. return 1;
  21. }else{
  22. /* 清除中断标志位 */
  23. iic_isr_flag = 0;
  24. return 0;
  25. }
  26. }
  27. /*************************************************************
  28. *
  29. * Function : 向AT24C08写入0~16个字节数据,如果超过16字节,可以分段调用该函数
  30. * Input : block 0~3
  31. * address 0~255
  32. * buffer 写入的数据 AT24C08的页缓冲区是16个字节,所有这里的循环最多也只能发送16个字节, 写入的数据必须位于同一页,页16字节对齐
  33. *
  34. **************************************************************/
  35. void iic_write_at24c08(u8 block, u8 address,u8 *buffer)
  36. {
  37. u8 i = 0;
  38. u8 length = 0;
  39. /* 设置从设备地址 */
  40. IICDS = 0xA0 + block * 2 ; /* 写入AT24C08地址 */
  41. IICSTAT = 0xf0; /* 主发送模式,发送从设备地址,发送完成触发中断 */
  42. /* 等待从设备应答 */
  43. if(iic_wait_ack() == 1){
  44. return;
  45. }
  46. /* 发送AT24C08内存地址 */
  47. IICDS = address; /* 发送完成触发中断 */
  48. /* 等待从设备应答 */
  49. if(iic_wait_ack() == 1){
  50. return;
  51. }
  52. /* 发送数据 */
  53. length = strlen((char *)buffer);
  54. for(i=0;i<length;i++)
  55. {
  56. IICDS = buffer[i]; /* 发送完成触发中断 */
  57. /* 等待从设备应答 */
  58. if(iic_wait_ack() == 1){
  59. return;
  60. }
  61. }
  62. IICSTAT = 0xD0; /* 主设备发送停止信号 */
  63. IICCON = 0xAF; /* 禁止TX/RX中断,为下次通讯做准备 */
  64. delay_ms(10); /* 这个延时一定要足够长,否则会出错。因为24c08在从sda上取得数据后,还需要一定时间的烧录过程 */
  65. }
4.4 从AT24C08读取n个字节数据

参考主机接收模式流程图编写代码,需要注意的是代码和流程图有部分出入,以下代码测试是可以运行的:

  1. /*************************************************************
  2. *
  3. * Function : 主设备接收到一个字节数据后,发送应答信号
  4. *
  5. **************************************************************/
  6. void iic_ack()
  7. {
  8. IICCON &= ~(1<<4); /* 清除IICCON挂起位,必须先清除 */
  9. /* 数据接收完成,会进入中断函数,设置标志位;一旦进入IIC中断,即可跳出该死循环 */
  10. while(iic_isr_flag == 0);
  11. /* 清除中断标志位 */
  12. iic_isr_flag = 0;
  13. /* 等待主设备发送应答信号 */
  14. IICCON |= (1<<7); /* 回应ACK */
  15. delay_us(10);
  16. }
  17. /*************************************************************
  18. *
  19. * Function : 从AT24C08读取n个字节数据
  20. * Input : block 0~3
  21. * address 0~255
  22. * buffer 缓冲区 长度为length + 1
  23. * length 需要读取的数据长度 最长255
  24. *
  25. **************************************************************/
  26. void iic_read_at24c08(u8 block,u8 address,u8 *buffer,u8 length)
  27. {
  28. u8 i=0;
  29. u8 temp;
  30. /* 设置从设备地址 */
  31. IICDS = 0xA0 + block * 2 ; /* 写入AT24C08地址 */
  32. IICSTAT = 0xf0; /* 主发送模式,发送从设备地址,发送完成触发中断 */
  33. /* 等待从设备应答 */
  34. if(iic_wait_ack() == 1){
  35. return;
  36. }
  37. /* 发送AT24C08内存地址 */
  38. IICDS = address; /* 发送完成触发中断 */
  39. /* 等待从设备应答 */
  40. if(iic_wait_ack() == 1){
  41. return;
  42. }
  43. /* 设置从设备地址 */
  44. IICDS = 0xA1 + block * 2 ; /* 写入AT24C08地址 */
  45. IICSTAT = 0xB0; /* 主接收模式吗,发送从设备地址,发送完成触发中断 */
  46. /* 等待从设备应答 */
  47. if(iic_wait_ack() == 1){
  48. return;
  49. }
  50. /* 先读取器件地址 */
  51. temp = IICDS; /* 接收到数据触发中断 */
  52. iic_ack(); /* 主机发送应答信号 */
  53. /* 再读取数据 */
  54. for(i=0;i<length;i++)
  55. {
  56. buffer[i] = IICDS; /* 接收到数据触发中断 */
  57. iic_ack(); /* 主机发送应答信号 */
  58. }
  59. buffer[i]='\0';
  60. IICSTAT = 0x90; /* 主设备发送停止信号 */
  61. IICCON = 0xAF; /* 禁止TX/RX中断,为下次通讯做准备 */
  62. delay_ms(10);
  63. }
4.5 中断函数
  1. /* 全局变量 */
  2. int iic_isr_flag = 0 ;
  3. /*************************************************************************
  4. *
  5. * Function : 中断源27 IIC 中断
  6. * 主设备字节数据发送完成会进入该函数
  7. * 主设备接收到从设备字节数据会进入该函数
  8. *
  9. *************************************************************************/
  10. void IIC_IRQHandler()
  11. {
  12. /* 设置中断标志位 */
  13. iic_isr_flag = 1;
  14. /* 清中断 */
  15. SRCPND |= BIT_IIC;
  16. INTPND |= BIT_IIC;
  17. }
4.6 测试代码
  1. #include "led.h"
  2. #include "common.h"
  3. #include "uart.h"
  4. #include "iic.h"
  5. int main()
  6. {
  7. u8 i = 0;
  8. u8 *write_buffer = "Dear my baby"; // 最长16个字符
  9. u8 read_buffer[32];
  10. vector_enable();
  11. // 初始化
  12. led_init();
  13. uart_init();
  14. delay_ms(1000);
  15. printf("iic init ...\r\n");
  16. iic_init();
  17. while(1)
  18. {
  19. printf("iic write ... \r\n");
  20. iic_write_at24c08(1, 0x00,write_buffer);
  21. led_turn(LED1);
  22. delay_ms(1000);
  23. printf("iic read ...\r\n");
  24. iic_read_at24c08(1, 0x00,read_buffer,sizeof(read_buffer)-1);
  25. printf("read value %s",read_buffer);
  26. printf("\r\n");
  27. }
  28. return 0;
  29. }

将代码下载到Nand Flash,启动开发板运行输出如下:

  1. iic init ...
  2. iic write ...
  3. iic read ...
  4. read value Dear my baby▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  5. iic write ...
  6. iic read ...
  7. read value Dear my baby▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  8. iic write ...
  9. iic read ...
  10. read value Dear my baby▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  11. iic write ...
  12. iic read ...
  13. read value Dear my baby▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

五、代码下载

Young / s3c2440_project【12.iic】

参考文章:

[1] IIC通信协议,搞懂这篇就够了

[2] s3c2440裸机-I2c编程-1.i2c协议

[3]s3c2440裸机-I2c编程-2.i2c控制器

[4]Keil5 平台 S3C2440裸机程序开发-----IIC

[5](6)s3c2440用I2C接口访问EEPROM

[6]mini2440 IIC 裸机程序记录

[7]mini2440裸机编程-----IIC—读写AT24C08

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/438241
推荐阅读
相关标签
  

闽ICP备14008679号