完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
花了好多的时间算是把软件I2C协议的代码写出来了。然后进阶,把HT32F5232利用I2C读写AT24C02的程序写出来了。这个过程中很坑的地方就是数据线SDA要一直在输入和输出这里切换,因为这个HT32输入时需要输入使能,就很麻烦。鉴于I2C之前有学过,这里就不贴出来关于I2C的详细解释了。
主要的代码: (1)I2C #include "iic.h" #include "systick.h" #include "usart.h" static void I2C_CKCU_Config() { CKCU_PeripClockConfig_TypeDef CCLOCK; CCLOCK.Bit.PA = 1; CCLOCK.Bit.AFIO = 1; CKCU_PeripClockConfig(CCLOCK, ENABLE); } static void I2C_GPIO_Config() { AFIO_GPxConfig(I2C_SCL_GPIO_ID, I2C_SCL_PIN, I2C_SCL_AFIO_MODE); AFIO_GPxConfig(I2C_SDA_GPIO_ID, I2C_SDA_PIN, I2C_SDA_AFIO_MODE); /* Configure the GPIO pin */ GPIO_PullResistorConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_PR_DISABLE); GPIO_DriveConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_DV_8MA); GPIO_DirectionConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_DIR_OUT); GPIO_PullResistorConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_PR_DISABLE); GPIO_DriveConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DV_8MA); GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_OUT); } void I2C_Initia() { I2C_CKCU_Config(); I2C_GPIO_Config(); /* 一定要先发一遍停止信号 */ I2C_Stop(); } void I2C_Start() { I2C_SCL_1(); I2C_SDA_1(); Systick_Delay_us(DELAYTIME); I2C_SDA_0(); Systick_Delay_us(DELAYTIME); I2C_SCL_0(); Systick_Delay_us(DELAYTIME); } void I2C_Stop() { I2C_SDA_0(); I2C_SCL_1(); Systick_Delay_us(DELAYTIME); I2C_SDA_1(); } /* 返回0表示正确应答,1表示无器件响应 */ u8 I2C_WaitAck() { u8 ack; I2C_SCL_1(); Systick_Delay_us(DELAYTIME); I2C_SDA_1(); Systick_Delay_us(DELAYTIME); I2C_SDA_Direction_Input(); I2C_SDA_InputConfig(); if( I2C_SDA_READ() ) { ack = 1; } else { ack = 0; } I2C_SCL_0(); Systick_Delay_us(DELAYTIME); return ack; } void I2C_SendByte(u8 _ucByte) { u8 i; /* 先发送字节的高位bit7 */ for (i = 0; i < 8; i++) { if (_ucByte & 0x80) { I2C_SDA_1(); } else { I2C_SDA_0(); } Systick_Delay_us(DELAYTIME); I2C_SCL_1(); Systick_Delay_us(DELAYTIME); I2C_SCL_0(); if (i == 7) { I2C_SDA_1(); // 释放总线 } _ucByte <<= 1; /* 左移一个bit */ Systick_Delay_us(DELAYTIME); } } u8 I2C_ReadByte() { u8 i; u8 data; data = 0; for(i = 0;i < 8;i++) { data <<= 1; I2C_SCL_1(); Systick_Delay_us(DELAYTIME); I2C_SDA_Direction_Input(); I2C_SDA_InputConfig(); if( I2C_SDA_READ() ) { data++; } I2C_SCL_0(); Systick_Delay_us(DELAYTIME); } return data; } // 返回值为0表示正确,返回1表示未探测到 u8 I2C_CheckDevice(u8 _Address) { // u8 time = 0; u8 ucAck = 0; I2C_Start(); I2C_SendByte(_Address | EEPROM_I2C_WR); ucAck =I2C_WaitAck(); I2C_SDA_Direction_Output(); I2C_Stop(); return ucAck; } void I2C_Ack() { I2C_SDA_0(); /* CPU驱动SDA = 0 */ Systick_Delay_us(DELAYTIME); I2C_SCL_1(); /* CPU产生1个时钟 */ Systick_Delay_us(DELAYTIME); I2C_SCL_0(); Systick_Delay_us(DELAYTIME); I2C_SDA_1(); /* CPU释放SDA总线 */ } void I2C_NAck() { I2C_SDA_1(); /* CPU驱动SDA = 1 */ Systick_Delay_us(DELAYTIME); I2C_SCL_1(); /* CPU产生1个时钟 */ Systick_Delay_us(DELAYTIME); I2C_SCL_0(); Systick_Delay_us(DELAYTIME); } (2)AT24C02 //1表示正常,0表示不正常 u8 EE_CheckOK() { if( I2C_CheckDevice(EEPROM_DEV_ADDR) == 0 ){ printf("rn发送地址完成,AT24C02在线n"); return 1; } else{ printf("rn发送地址完成,AT24C02离线n"); I2C_Stop(); return 0; } } /********************************************** * _usAddress : 起始地址 * * _usSize : 数据长度,单位为字节 * * _pWriteBuf : 存放读到的数据的缓冲区指针 * ***********************************************/ /* 为了提高连续写的效率: 本函数采用page wirte操作。*/ /* _usSize大于8会在第四步卡住 */ u8 EE_WriteBytes(u8 *_pWriteBuf, u16 _usAddress, u16 _usSize) { u16 i,m; u16 usAddr; usAddr = _usAddress; for (i = 0; i < _usSize; i++) { /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */ if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0) { /* 启动内部写操作 */ I2C_Stop(); for (m = 0; m < 1000; m++) { I2C_Start(); /* 第2步:高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR); if (I2C_WaitAck() == 0) { break; } } I2C_SDA_Direction_Output(); /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */ I2C_SendByte((uint8_t)usAddr); if(I2C_WaitAck() != 0) { time++; if( time ==WAITMAX ){ printf("写入数据第四步无应答"); return 0; } } time = 0; } I2C_SDA_Direction_Output(); /* 第6步:开始写入数据 */ I2C_SendByte(_pWriteBuf); /* 第7步:发送ACK */ if(I2C_WaitAck() != 0) { time++; if( time ==WAITMAX ){ printf("写入数据第七步无应答"); return 0; } } time = 0; I2C_SDA_Direction_Output(); usAddr++; /* 地址增1 */ } I2C_SDA_Direction_Output(); /* 命令执行成功,发送I2C总线停止信号 */ I2C_Stop(); return 1; } u8 EE_ReadBytes(u8 *_pReadBuf, u16 _usAddress, u16 _usSize) { u16 i; u32 time = 0; I2C_Initia(); I2C_Start(); /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR); if ( I2C_WaitAck() != 0 ) { time++; if(time == WAITMAX){ printf("rn读取数据第二步无应答rn"); return 0; } } I2C_SDA_Direction_Output(); time = 0; /* 发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */ I2C_SendByte((u8)_usAddress); if ( I2C_WaitAck() != 0 ) { time++; if(time == WAITMAX){ printf("rn读取数据第四步EEPROM无应答rn"); /* EEPROM器件无应答 */ return 0; } } I2C_SDA_Direction_Output(); time = 0; /* 重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */ I2C_Start(); /* 发起控制字节,高7bit是地址,bit0是读写控制位 */ I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD); if ( I2C_WaitAck() != 0 ) { time++; if(time == WAITMAX){ printf("rn读取数据第七步EEPROM无应答rn"); /* EEPROM器件无应答 */ return 0; } } time = 0; I2C_SDA_Direction_Input(); I2C_SDA_InputConfig(); /* 第9步:循环读取数据 */ for (i = 0; i < _usSize; i++) { _pReadBuf = I2C_ReadByte(); /* 读1个字节 */ /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */ if (i != _usSize - 1) { I2C_SDA_Direction_Output(); I2C_Ack(); /* CPU产生ACK信号(驱动SDA = 0) */ I2C_SDA_Direction_Input(); I2C_SDA_InputConfig(); } else { I2C_SDA_Direction_Output(); I2C_NAck(); /* CPU产生NACK信号(驱动SDA = 1) */ } } I2C_Stop(); printf("rn读取数据成功rn"); return 1; /* 执行成功 */ } 对硬件方面(如引脚、模式选择等的封装): #ifndef _IIC_H #define _IIC_H #include "ht32f5xxxx_01.h" #define EEPROM_I2C_WR 0 /* 写控制bit */ #define EEPROM_I2C_RD 1 /* 读控制bit */ #define EEPROM_DEV_ADDR 0xA0 /* 24xx02的设备地址 */ #define EEPROM_PAGE_SIZE 8 /* 24xx02的页面大小 */ #define EEPROM_SIZE 256 /* 24xx02总容量 */ /* 定义I2C的SCL连接的GPIO端口 */ #define I2C_SCL_GPIO_ID GPIO_PA /* GPIO端口 */ #define I2C_SCL_PIN GPIO_PIN_0 /* 连接到SCL时钟线的GPIO */ #define I2C_SCL_AFIO_MODE AFIO_FUN_GPIO #define I2C_SCL_PORT HT_GPIOA /* 定义I2C的SDA连接的GPIO端口 */ #define I2C_SDA_GPIO_ID GPIO_PA /* GPIO端口 */ #define I2C_SDA_PIN GPIO_PIN_1 /* 连接到SCL时钟线的GPIO */ #define I2C_SDA_AFIO_MODE AFIO_FUN_GPIO #define I2C_SDA_PORT HT_GPIOA #define I2C_SCL_1() GPIO_SetOutBits(I2C_SCL_PORT, I2C_SCL_PIN) #define I2C_SCL_0() GPIO_ClearOutBits(I2C_SCL_PORT, I2C_SCL_PIN) #define I2C_SDA_1() GPIO_SetOutBits(I2C_SDA_PORT, I2C_SDA_PIN) #define I2C_SDA_0() GPIO_ClearOutBits(I2C_SDA_PORT, I2C_SDA_PIN) #define I2C_SDA_Direction_Input() GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_IN) #define I2C_SDA_InputConfig() GPIO_InputConfig(I2C_SDA_PORT, I2C_SDA_PIN, ENABLE) #define I2C_SDA_InputDisable() GPIO_InputConfig(I2C_SDA_PORT, I2C_SDA_PIN, DISABLE) #define I2C_SDA_Direction_Output() GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_OUT) #define I2C_SDA_READ() GPIO_ReadInBit(I2C_SDA_PORT, I2C_SDA_PIN) #define WAITMAX 2000 #define DELAYTIME 2 void I2C_Initia(void); void I2C_Start(void); void I2C_Stop(void); u8 I2C_WaitAck(void); void I2C_SendByte(u8 _ucByte); u8 I2C_ReadByte(void); void I2C_Ack(void); void I2C_NAck(void); //FlagStatus I2C_CheckOK(void); u8 I2C_CheckDevice(u8 _Address); u8 EE_CheckOK(void); u8 EE_WriteBytes(u8 *_pWriteBuf, u16 _usAddress, u16 _usSize); u8 EE_ReadBytes(u8 *_pReadBuf, u16 _usAddress, u16 _usSize); |
|
|
|
只有小组成员才能发言,加入小组>>
3310 浏览 9 评论
2991 浏览 16 评论
3492 浏览 1 评论
9057 浏览 16 评论
4086 浏览 18 评论
1176浏览 3评论
604浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
597浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2334浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1895浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 18:34 , Processed in 1.197604 second(s), Total 78, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号