【啰嗦2句】
本来想I2C有什么难的,连接I2C是51单片机入门就必学的了,没什么可讲。
但是,万一有人想直接抄作业,总得有地方复制粘贴吧?总得给他借鉴一些吧,----直到他知道抄作业是没用的。
【硬件简介】
AT24C02是一款自带2Kbit(256Byte)的相当成熟的EEPROM芯片,我最早接触的是ATMAL公司的,当时仍未入行,后来学单片机又是从AT89C52学起,又是这家公司的,于是对这个品牌有深刻印象,他们家的产品品质优秀,值得尊敬。该芯片的通信采用I2C总线,操作容易,资料遍地。国产同型号芯片也非常多,目前用过的没遇到坑,估计闭眼买吧。
而我们的主角AT32F421C8T7主控,操作I2C跟STM32大同小异,本文更着重小异部分的讲解。
需要注意:AT24C02虽然电压宽域,但是速度跟电压成正比,也就是说电压越低,速度要慢些,电压越高,速度要快些。手册有提到。并且有的型号支持最低1.8V(达不到最高速度),有的最低可能要2.7V。例如:1.8V有的只能100Kb/s,2.7V-5V才能到400Kb/s,调试中遇到问题或许是这个原因。
【原理图】
【演示内容】
AT32F421C8T7单片机通过I2C2跟AT2402相连,通过UART1跟电脑串口连接,上位机发的内容按顺序存储在EEPROM,单片机存储后按位读出并返回给上位机。结束符为"\n"。
【主要源码】
本源码参考自雅特力官方源码:
AT32F421_Firmware_Library_V2.1.2\project\at_start_f421\examples\i2c\eeprom
官方例程的代码没有修改的就不展示,比如: i2c_application.c、i2c_application.h
文件名:eeprom.c
#include "includes.h"
//这个必须定义,大概是用于区分哪个I2C对象
i2c_handle_type hi2cx;
void error_handler(uint32_t error_code);
void i2c_lowlevel_init(i2c_handle_type* hi2c);
/**
* @brief error handler program
* @param i2c_status
* @retval none
*/
void error_handler(uint32_t error_code)
{
//while(1)
//{
//at32_led_toggle(LED2);
//delay_ms(500);
//}
}
/**
* @brief initializes peripherals used by the i2c.
* @param none
* @retval none
*/
void i2c_lowlevel_init(i2c_handle_type* hi2c)
{
gpio_init_type gpio_initstructure;
if(hi2c->i2cx == I2Cx_PORT)
{
/* i2c periph clock enable */
crm_periph_clock_enable(I2Cx_CLK, TRUE);
crm_periph_clock_enable(I2Cx_SCL_GPIO_CLK, TRUE);
crm_periph_clock_enable(I2Cx_SDA_GPIO_CLK, TRUE);
/* gpio configuration */
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
/* configure i2c pins: scl */
gpio_initstructure.gpio_pins = I2Cx_SCL_PIN;
gpio_init(I2Cx_SCL_GPIO_PORT, &gpio_initstructure);
gpio_pin_mux_config(I2Cx_SCL_GPIO_PORT, I2Cx_SCL_PIN_SOURCE, I2Cx_SCL_PIN_MUX_NUM);
/* configure i2c pins: sda */
gpio_initstructure.gpio_pins = I2Cx_SDA_PIN;
gpio_init(I2Cx_SDA_GPIO_PORT, &gpio_initstructure);
gpio_pin_mux_config(I2Cx_SDA_GPIO_PORT, I2Cx_SDA_PIN_SOURCE, I2Cx_SDA_PIN_MUX_NUM);
/* configure and enable i2c dma channel interrupt */
nvic_irq_enable(I2Cx_DMA_TX_IRQn, 0, 0);
nvic_irq_enable(I2Cx_DMA_RX_IRQn, 0, 0);
/* configure and enable i2c interrupt */
nvic_irq_enable(I2Cx_EVT_IRQn, 0, 0);
nvic_irq_enable(I2Cx_ERR_IRQn, 0, 0);
/* i2c dma tx and rx channels configuration */
/* enable the dma clock */
crm_periph_clock_enable(I2Cx_DMA_CLK, TRUE);
/* i2c dma channel configuration */
dma_reset(hi2c->dma_tx_channel);
dma_reset(hi2c->dma_rx_channel);
hi2c->dma_tx_channel = I2Cx_DMA_TX_CHANNEL;
hi2c->dma_rx_channel = I2Cx_DMA_RX_CHANNEL;
dma_default_para_init(&hi2c->dma_init_struct);
hi2c->dma_init_struct.peripheral_inc_enable = FALSE;
hi2c->dma_init_struct.memory_inc_enable = TRUE;
hi2c->dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
hi2c->dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
hi2c->dma_init_struct.loop_mode_enable = FALSE;
hi2c->dma_init_struct.priority = DMA_PRIORITY_LOW;
hi2c->dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
dma_init(hi2c->dma_tx_channel, &hi2c->dma_init_struct);
dma_init(hi2c->dma_rx_channel, &hi2c->dma_init_struct);
i2c_init(hi2c->i2cx, I2C_FSMODE_DUTY_2_1, I2Cx_SPEED);
i2c_own_address1_set(hi2c->i2cx, I2C_ADDRESS_MODE_7BIT, I2Cx_ADDRESS);
}
}
/****************************************************************
*函数:AT24CXX_Init
*功能:初始化EEPROM
*参数:无
*
****************************************************************/
void AT24CXX_Init(void)
{
hi2cx.i2cx = I2Cx_PORT;
i2c_config(&hi2cx);
}
/****************************************************************
*函数:AT24CXX_Write
*功能:写入EEPROM
*参数:buf--写入的内容,iAddress--地址,length--写入的内容长度
*
****************************************************************/
void AT24CXX_Write(uint16_t iAddress, uint8_t *buf, uint32_t length)
{
u8 i=0;
i2c_status_type i2c_status;
if(iAddress > EE_TYPE)
{
return;
}
if(iAddress + length >EE_TYPE)
{
length=EE_TYPE-iAddress;
}
for(i=0;i<length;i+=8)
{
if(length-i>8)
{
if((i2c_status = i2c_memory_write(&hi2cx, I2C_MEM_ADDR_WIDIH_8, I2Cx_ADDRESS, iAddress+i, &buf[i], 8, I2C_TIMEOUT)) != I2C_OK)
{
error_handler(i2c_status);
}
}
else
{
if((i2c_status = i2c_memory_write(&hi2cx, I2C_MEM_ADDR_WIDIH_8, I2Cx_ADDRESS, iAddress+i, &buf[i], length-i, I2C_TIMEOUT)) != I2C_OK)
{
error_handler(i2c_status);
}
}
delay_ms(5);
}
}
/****************************************************************
*函数:AT24CXX_Read
*功能:读出EEPROM
*参数:buf--读出的内容,iAddress--地址,length--读出的内容长度
*
****************************************************************/
void AT24CXX_Read(uint16_t iAddress, uint8_t *buf, int32_t length)
{
u8 i=0;
i2c_status_type i2c_status;
for(i=0;i<length;i+=8)
{
if(length-i>8)
{
if((i2c_status = i2c_memory_read(&hi2cx, I2C_MEM_ADDR_WIDIH_8, I2Cx_ADDRESS, iAddress+i, &buf[i], 8, I2C_TIMEOUT)) != I2C_OK)
{
error_handler(i2c_status);
}
}
else
{
if((i2c_status = i2c_memory_read(&hi2cx, I2C_MEM_ADDR_WIDIH_8, I2Cx_ADDRESS, iAddress+i, &buf[i], length-i, I2C_TIMEOUT)) != I2C_OK)
{
error_handler(i2c_status);
}
}
delay_ms(5);
}
}
文件名:eeprom.h
#ifndef __EEPROM_H
#define __EEPROM_H
#define AT24C01 127
#define AT24C02 255 //定义芯片容量
#define AT24C04 511
#define AT24C08 1023
#define AT24C16 2047
#define AT24C32 4095
#define AT24C64 8191
#define AT24C128 16383
#define AT24C256 32767
#define EE_TYPE AT24C02 //更换芯片记得改这个型号
#define I2C_TIMEOUT 0xFFFFFFFF
#define I2Cx_SPEED 100000
#define I2Cx_ADDRESS 0xA0
#define I2Cx_PORT I2C2
#define I2Cx_CLK CRM_I2C2_PERIPH_CLOCK
#define I2Cx_SCL_PIN GPIO_PINS_6
#define I2Cx_SCL_PIN_SOURCE GPIO_PINS_SOURCE6
#define I2Cx_SCL_PIN_MUX_NUM GPIO_MUX_0
#define I2Cx_SCL_GPIO_PORT GPIOF
#define I2Cx_SCL_GPIO_CLK CRM_GPIOF_PERIPH_CLOCK
#define I2Cx_SDA_PIN GPIO_PINS_7
#define I2Cx_SDA_PIN_SOURCE GPIO_PINS_SOURCE7
#define I2Cx_SDA_PIN_MUX_NUM GPIO_MUX_0
#define I2Cx_SDA_GPIO_PORT GPIOF
#define I2Cx_SDA_GPIO_CLK CRM_GPIOF_PERIPH_CLOCK
#define I2Cx_DMA_CLK CRM_DMA1_PERIPH_CLOCK
#define I2Cx_DMA_TX_CHANNEL DMA1_CHANNEL4
#define I2Cx_DMA_TX_IRQn DMA1_Channel5_4_IRQn
#define I2Cx_DMA_RX_CHANNEL DMA1_CHANNEL5
#define I2Cx_DMA_RX_IRQn DMA1_Channel5_4_IRQn
#define I2Cx_EVT_IRQn I2C2_EVT_IRQn
#define I2Cx_ERR_IRQn I2C2_ERR_IRQn
void AT24CXX_Init(void);
void AT24CXX_Write(uint16_t iAddress, uint8_t *buf, uint32_t length);
void AT24CXX_Read(uint16_t iAddress, uint8_t *buf, int32_t length);
#endif
文件名:main.c
/******************** (C) COPYRIGHT 2024 移动中的鸭子 ********************
* File Name : main.c
* Author : CZY Application Team
* Version : V1.0.0
* Date : 2024-08-04
* Description : 演示入口及串口处理
*************************************************************************/
#include "includes.h"
/*******************************************************************************
* 函数名 : main
* 描述 : 入口函数,你懂的
* 说明 :
*******************************************************************************/
int main(void)
{
u8 temp[100];
Set_System(); //这里是其他时钟等常用初始化
USART1_Init(); //初始化串口1,用于跟上位机通信
AT24CXX_Init(); //初始化AT24C02
while(1)
{
if(PC_ReceiveFlag==1) //说明触发中断并且接收到"\r\n"
{
//接收完成后写入EEPROM,从地址0开始
AT24CXX_Write(0,PC_RevBuffer,PC_RevLength);
//写完又读出到temp缓存,从地址0开始
AT24CXX_Read(0,temp,PC_RevLength);//如果写入失败,这里读出的应该全00或全FF,
//直接回发给上位机
USART1_SendByte(temp,PC_RevLength);
PC_ClrRevBuffer();
PC_ReceiveFlag=0; //清空接收标记
}
}
}
文件名:pc.c
/******************** (C) COPYRIGHT 2024 移动中的鸭子 ********************
* File Name : main.c
* Author : CZY Application Team
* Version : V1.0.0
* Date : 2024-08-04
* Description : 演示串口1接收
*************************************************************************/
#include "includes.h"
unsigned char PC_RevBuffer[PC_MAX]; //端口接收数据缓冲区
unsigned char PC_RevLength=0; //已接收的长度
unsigned char PC_ReceiveFlag=0; //接收完整包标记
/*************************************************************************
*函 数 名: PC_ReceiveHandle
*功能描述: 处理电脑串口接收到的数据
*输 入: data:接收的字节
*输 出:
***************************************************************************/
void PC_ReceiveHandle(u8 dat)
{
PC_RevBuffer[PC_RevLength]=dat;
PC_RevLength++;
if(dat=='\n') //当收到回车换行符后,标记为接收完成,main函数会处理。
{
PC_ReceiveFlag=1;
}
}
/*************************************************************************
*函 数 名: PC_ClrRevBuffer
*功能描述: 清空接收缓存
*输 入:
*输 出:
***************************************************************************/
void PC_ClrRevBuffer(void)
{
memset(PC_RevBuffer,0x00,PC_MAX);
PC_RevLength=0;
}
文件名:pc.h
#ifndef __PC_H
#define __PC_H
#define PC_MAX 100
extern unsigned char PC_ReceiveFlag;
extern unsigned char PC_RevBuffer[PC_MAX]; //端口接收数据缓冲区
extern unsigned char PC_RevLength;
void PC_ReceiveHandle(unsigned char dat);
void PC_ClrRevBuffer(void);
#endif
文件名:at32f421_int.c(大部分是官方源码,最后部分4个I2Cx开头是I2C重点代码)
/**
**************************************************************************
* @file at32f421_int.c
* @brief main interrupt service routines.
**************************************************************************
* Copyright notice & Disclaimer
*
* The software Board Support Package (BSP) that is made available to
* download from Artery official website is the copyrighted work of Artery.
* Artery authorizes customers to use, copy, and distribute the BSP
* software and its related documentation for the purpose of design and
* development in conjunction with Artery microcontrollers. Use of the
* software is governed by this copyright notice and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
* GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
* TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
* STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
* INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
*
**************************************************************************
*/
/* includes ------------------------------------------------------------------*/
#include "at32f421_int.h"
#include "i2c_application.h"
extern i2c_handle_type hi2cx;
#define I2Cx_DMA_RX_TX_IRQHandler DMA1_Channel5_4_IRQHandler
#define I2Cx_EVT_IRQHandler I2C2_EVT_IRQHandler
#define I2Cx_ERR_IRQHandler I2C2_ERR_IRQHandler
irqCallback_ts *irqCallback_extiLine1;
irqCallback_ts *firstIrqCallback_extiLine1;
/** @addtogroup AT32F421_periph_examples
* @{
*/
/** @addtogroup 421_SPI_fullduplex_polling
* @{
*/
/**
* @brief this function handles nmi exception.
* @param none
* @retval none
*/
void NMI_Handler(void)
{
}
/**
* @brief this function handles hard fault exception.
* @param none
* @retval none
*/
void HardFault_Handler(void)
{
/* go to infinite loop when hard fault exception occurs */
while(1)
{
}
}
/**
* @brief this function handles memory manage exception.
* @param none
* @retval none
*/
void MemManage_Handler(void)
{
/* go to infinite loop when memory manage exception occurs */
while(1)
{
}
}
/**
* @brief this function handles bus fault exception.
* @param none
* @retval none
*/
void BusFault_Handler(void)
{
/* go to infinite loop when bus fault exception occurs */
while(1)
{
}
}
/**
* @brief this function handles usage fault exception.
* @param none
* @retval none
*/
void UsageFault_Handler(void)
{
/* go to infinite loop when usage fault exception occurs */
while(1)
{
}
}
/**
* @brief this function handles svcall exception.
* @param none
* @retval none
*/
void SVC_Handler(void)
{
}
/**
* @brief this function handles debug monitor exception.
* @param none
* @retval none
*/
void DebugMon_Handler(void)
{
}
/**
* @brief this function handles pendsv_handler exception.
* @param none
* @retval none
*/
void PendSV_Handler(void)
{
}
/**
* @brief this function handles systick handler.
* @param none
* @retval none
*/
void SysTick_Handler(void)
{
}
/**
* @brief this function handles dma interrupt request.
* @param none
* @retval none
*/
void I2Cx_DMA_RX_TX_IRQHandler(void)
{
i2c_dma_rx_irq_handler(&hi2cx);
i2c_dma_tx_irq_handler(&hi2cx);
}
/**
* @brief this function handles i2c event interrupt request.
* @param none
* @retval none
*/
void I2Cx_EVT_IRQHandler(void)
{
i2c_evt_irq_handler(&hi2cx);
}
/**
* @brief this function handles i2c error interrupt request.
* @param none
* @retval none
*/
void I2Cx_ERR_IRQHandler(void)
{
i2c_err_irq_handler(&hi2cx);
}
文件名:usart.c
#include "includes.h"
/****************************************************************
*函 数 名:USART1_Init
*功能说明:串口1配置
*参 数:
*输 出:
**********************************************************/
void USART1_Init(void)
{
gpio_init_type gpio_init_struct;
/* enable the usart1 and gpio clock */
crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_init_struct);
/* configure the usart1 tx/rx pin */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_pins = GPIO_PINS_9 | GPIO_PINS_10;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOA, &gpio_init_struct);
/* config usart1 iomux */
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE9, GPIO_MUX_1);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE10, GPIO_MUX_1);
/* config usart1 nvic interrupt */
nvic_irq_enable(USART1_IRQn, 0, 0);
/* configure usart1 param */
usart_init(USART1, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
usart_transmitter_enable(USART1, TRUE);
usart_receiver_enable(USART1, TRUE);
/* enable usart1 and usart1 interrupt */
usart_interrupt_enable(USART1, USART_RDBF_INT, TRUE);
usart_enable(USART1, TRUE);
}
/**
* @brief this function handles usart1 handler.
* @param none
* @retval none
*/
void USART1_IRQHandler(void)
{
if(USART1->ctrl1_bit.rdbfien != RESET)
{
if(usart_flag_get(USART1, USART_RDBF_FLAG) != RESET)
{
//读出的字节转给PC.c专门处理
PC_ReceiveHandle(usart_data_receive(USART1));
}
}
if(USART1->ctrl1_bit.tdbeien != RESET)
{
if(usart_flag_get(USART1, USART_TDBE_FLAG) != RESET)
{
}
}
}
void USART1_Send(unsigned char dat)
{
while(usart_flag_get(USART1, USART_TDBE_FLAG) == RESET);
usart_data_transmit(USART1, dat);
}
void USART1_SendByte(unsigned char *p,unsigned int length)
{
while (length--)
{
USART1_Send(*p++);
}
}
文件名:usart.h
#ifndef __UART_H
#define __UART_H
void USART1_Init(void);
void USART1_SendByte(unsigned char *p,unsigned int length);
#endif
【实测效果】
以上是本文全部内容,讲解不是很详细,可能需要网友具备部分开发经验,才好理解。