IIC可获取资源
硬件模块
IIC协议:UM10204-I2C-bus specification and user manual-Rev. 7.0 — 1 October 2021
《UM10204-I2C-bus specification and user manual-Rev. 7.0 — 1 October 2021》
stm32f10x的IIC带DMA的数据传输模式
DMA如何跟外设模块自动通信?
【DMA控制器功能描述】:
13.3.1 DMA transactions
After an event, the peripheral sends a request signal to the DMA Controller. The DMA controller serves the request depending on the channel priorities.
【IIC控制器的DMA功能描述】:
26.3.7 DMA requests
DMA requests (when enabled) are generated only for data transfer. DMA requests are generated by Data register becoming empty in transmission and Data register becoming full in reception.
【SPI控制器的DMA功能描述】:
25.3.9 SPI communication using DMA (direct memory addressing)
· In transmission, a DMA request is issued each time TXE is set to 1. The DMA then
writes to the SPI_DR register (this clears the TXE flag).
· In reception, a DMA request is issued each time RXNE is set to 1. The DMA then reads
the SPI_DR register (this clears the RXNE flag).
【USART控制器的DMA功能描述】:
27.3.13 Continuous communication using DMA
The USART is capable of continuing communication using the DMA. The DMA requests for Rx buffer and Tx buffer are generated independently.
Transmission using DMA
DMA mode can be enabled for transmission by setting DMAT bit in the USART_CR3 register. Data is loaded from a SRAM area configured using the DMA peripheral (refer to the DMA specification) to the USART_DR register whenever the TXE bit is set.
Reception using DMA
DMA mode can be enabled for reception by setting the DMAR bit in USART_CR3 register. Data is loaded from the USART_DR register to a SRAM area configured using the DMA peripheral (refer to the DMA specification) whenever a data byte is received.
总结1:DMA控制器一旦接收DMA数据传输请求信号,便开始数据传输;需要通信的外设模块,自洽发送DMA数据传输请求信号:数据传输过程中,外设模块的数据寄存器读数据非空、写数据空事件时,自动发送DMA请求。硬件互联特性,硬件实现,软件无需考虑。
总结2:编程视角:在DMA外设模块通信时,DMA只响应数据传输请求信号,在数据传输前提前配置好DMA功能就好了。传输过程的协议执行细节(何时发送数据?等)通信外设模块自洽。
补充3:NXP-LPC1788芯片的IIC模块不支持DAM传输。
shift register
嵌入式外设编程模式
嵌入式裸机编程视图:
1. 直接操作寄存器模式:类似51单片机的少量寄存器,应用代码直接操作寄存器;
2. 外设模块元功能抽象模式:stm32f10x的标准外设库:对硬件外设模块元功能进行了抽象,属于1.寄存器操作模式的+1进化版;
2.1 基于这种传统的标准外设库,一般需要再编写一个对应的routine_iic.c进一步封装外设库函数,直接提供给应用代码调用;
一般标准库不包含中断服务函数,因此需要代码工程师自定义实现,并与 routine_iic.c(比如)耦合,直接提供给应用层代码使用。
3. 外设驱动的最高级别抽象库函数:比如stm32f10x的HAL版本库函数、STM32Cube_FW_F1_V1.8.0,直接面相应用层代码提供的全能库函数集合。以轮询、中断、DMA模式提供接口,包括硬件高级使用功能的抽象级别函数和中断服务函数,也就是2+2.1版本的集合,直接提供给应用层代码使用。
代码举例:
/**
******************************************************************************
* @file stm32f1xx_hal_i2c.c
* @author MCD Application Team
* @version V1.0.1
* @date 31-July-2015
* @brief I2C HAL module driver.
* This file provides firmware functions to manage the following
* functionalities of the Inter Integrated Circuit (I2C) peripheral:
* + Initialization and de-initialization functions
* + IO operation functions
* + Peripheral Control functions
* + Peripheral State functions
******************************************************************************
包含:
1. HAL(硬件抽象层):面相应用层级别的硬件模块代码抽象APIs:提供(轮询、中断、DMA模式的APIs),比如:
1.1 @brief Transmits in master mode an amount of data in blocking mode.
HAL_I2C_Master_Transmit 函数;
1.2 @brief Transmit in master mode an amount of data in no-blocking mode with Interrupt
HAL_I2C_Master_Transmit_IT 函数;
1.3 @brief Transmit in master mode an amount of data in no-blocking mode with DMA
HAL_I2C_Master_Transmit_DMA 函数;
1.4 @brief Write an amount of data in blocking mode to a specific memory address
HAL_I2C_Mem_Write 函数;
1.5 @brief Read an amount of data in no-blocking mode with Interrupt from a specific memory address
HAL_I2C_Mem_Write_IT 函数;
1.6 @brief Write an amount of data in no-blocking mode with DMA to a specific memory address
HAL_I2C_Mem_Write_DMA 函数;
2. 中断服务函数:
2.1 void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c);
void HAL_I2C_ER_IRQHandler(I2C_HandleTypeDef *hi2c);
stm32f10x的IIC代码
1. 思考:
1.1 stm32f10x的IIC模块与LPC1788的IIC模块肯定都能兼容I2C标准;
1.2 stm32f10x的IIC模块与LPC1788的IIC模块兼容IIC标准部分的硬件设计实现结构不完全同;
1.3 模块的编程过程的细节与模块的硬件设计的工作流有关;
1.3.1 stm32f10x的IIC模块有两个:一个数据寄存器、一个移位数据寄存器;
1.3.2 stm32f10x的IIC模块支持带DMA传输,LPC1788的IIC模块不支持带DMA传输;
1.3.3 stm32f10x的IIC传输速率上,只支持标准模式 100k bps 和快速模式 400k bps;
1.4 stm32f10x的IIC模块的编程模式: 轮询/中断/DMA的主从读写模式;
1.4.1 模块的编程一般支持带中断行为的操作;
1.4.2 模块的编程,涉及到数据传输,可以增加DMA特性;
1.4.3 IIC的NVIC中断有两个:事件中断,总线错误中断;
1.4.4 当 I2C_CR2->DMAEN(bit11)=1 且 TxE=1 or RxNE =1时,IIC模块向DMA发送数据传输的请求信号;
1.4.5 在DMA收到外设IIC模块发来的请求后,便按照事先配置,为其提供数据传输服务,DMA有自己的中断。
1.4.6 DMA数据任务传输完成后,向IIC控制器发送 EOT(End of Transfer) 传输完成的信号,且若开启则产生DMA的传输完成中断。
2. 源代码-常规routine版本:
main.c 文件
/* 私有变量*/
ErrorStatus HSEStartUpStatus;
/* Buffer of data to be received by I2C1 */
uint8_t Buffer_Rx1[255];
/* Buffer of data to be transmitted by I2C1 */
uint8_t Buffer_Tx1[255] = {0x5, 0x6,0x8,0xA};
/* Buffer of data to be received by I2C2 */
uint8_t Buffer_Rx2[255];
/* Buffer of data to be transmitted by I2C2 */
uint8_t Buffer_Tx2[255] = {0xF, 0xB, 0xC,0xD};
extern __IO uint8_t Tx_Idx1 , Rx_Idx1;
extern __IO uint8_t Tx_Idx2 , Rx_Idx2;
/* 主程序 */
int main(void)
{
NVIC_Configuration();
I2C_LowLevel_Init(I2C2);
I2C_LowLevel_Init(I2C1);
/* 使用stm32的IIC1作为主机,与另一个stm32的IIC1进行通信 */
while(1)
{
I2C_Master_BufferWrite(I2C1, Buffer_Tx1,120,Interrupt, 0x28);
I2C_Master_BufferRead(I2C1,Buffer_Rx1,120,Polling, 0x28);
}
/* IIC1作为从机 */
/*! 当作为从机,使用DMA,解注释
//#define SLAVE_DMA_USE in the stm32f10x_it.c file.*/
/*
I2C_Slave_BufferReadWrite(I2C1, DMA);
while(1);
*/
}
/**
* @brief Configures NVIC and Vector Table base location.
* @param None
* @retval : None
*/
void NVIC_Configuration(void)
{
/* 1 bit for pre-emption priority, 3 bits for subpriority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_SetPriority(I2C1_EV_IRQn, 0x00);
NVIC_EnableIRQ(I2C1_EV_IRQn);
NVIC_SetPriority(I2C1_ER_IRQn, 0x01);
NVIC_EnableIRQ(I2C1_ER_IRQn);
NVIC_SetPriority(I2C2_EV_IRQn, 0x00);
NVIC_EnableIRQ(I2C2_EV_IRQn);
NVIC_SetPriority(I2C2_ER_IRQn, 0x01);
NVIC_EnableIRQ(I2C2_ER_IRQn);
}
@file OptimizedI2Cexamples/src/I2CRoutines.c 文件:
/**
******************************************************************************
* @file OptimizedI2Cexamples/src/I2CRoutines.c
* @author MCD Application Team
* @version V4.0.0
* @date 06/18/2010
* @brief Contains the I2Cx slave/Master read and write routines.
******************************************************************************
* @copy
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>© COPYRIGHT 2010 STMicroelectronics</center></h2>
*/
/* Includes ------------------------------------------------------------------*/
#include "I2CRoutines.h"
/** @addtogroup Optimized I2C examples
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
DMA_InitTypeDef I2CDMA_InitStructure;
__IO uint32_t I2CDirection = I2C_DIRECTION_TX;
__IO uint32_t NumbOfBytes1;
__IO uint32_t NumbOfBytes2;
__IO uint8_t Address;
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
* @brief Reads buffer of bytes from the slave.
* @param pBuffer: Buffer of bytes to be read from the slave.
* @param NumByteToRead: Number of bytes to be read by the Master.
* @param Mode: Polling or DMA or Interrupt having the highest priority in the application.
* @param SlaveAddress: The address of the slave to be addressed by the Master.
* @retval : None.
*/
Status I2C_Master_BufferRead(I2C_TypeDef* I2Cx, uint8_t* pBuffer, uint32_t NumByteToRead, I2C_ProgrammingModel Mode, uint8_t SlaveAddress)
{
__IO uint32_t temp = 0;
__IO uint32_t Timeout = 0;
/* Enable I2C errors interrupts (used in all modes: Polling, DMA and Interrupts */
I2Cx->CR2 |= I2C_IT_ERR;
if (Mode == DMA) /* I2Cx Master Reception using DMA */
{
/* Configure I2Cx DMA channel */
I2C_DMAConfig(I2Cx, pBuffer, NumByteToRead, I2C_DIRECTION_RX);
/* Set Last bit to have a NACK on the last received byte */
I2Cx->CR2 |= CR2_LAST_Set;
/* Enable I2C DMA requests */
I2Cx->CR2 |= CR2_DMAEN_Set;
Timeout = 0xFFFF;
/* Send START condition */
I2Cx->CR1 |= CR1_START_Set;
/* Wait until SB flag is set: EV5 */
while ((I2Cx->SR1&0x0001) != 0x0001)
{
if (Timeout-- == 0)
return Error;
}
Timeout = 0xFFFF;
/* Send slave address */
/* Set the address bit0 for read */
SlaveAddress |= OAR1_ADD0_Set;
Address = SlaveAddress;
/* Send the slave address */
I2Cx->DR = Address;
/* Wait until ADDR is set: EV6 */
while ((I2Cx->SR1&0x0002) != 0x0002)
{
if (Timeout-- == 0)
return Error;
}
/* Clear ADDR flag by reading SR2 register */
temp = I2Cx->SR2;
if (I2Cx == I2C1)
{
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC7));
/* Disable DMA Channel */
DMA_Cmd(I2C1_DMA_CHANNEL_RX, DISABLE);
/* Clear the DMA Transfer Complete flag */
DMA_ClearFlag(DMA1_FLAG_TC7);
}
else /* I2Cx = I2C2*/
{
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC5));
/* Disable DMA Channel */
DMA_Cmd(I2C2_DMA_CHANNEL_RX, DISABLE);
/* Clear the DMA Transfer Complete flag */
DMA_ClearFlag(DMA1_FLAG_TC5);
}
/* Program the STOP */
I2Cx->CR1 |= CR1_STOP_Set;
/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
while ((I2Cx->CR1&0x200) == 0x200);
}
else if (Mode == Polling) /* I2Cx Master Reception using Polling */
{
if (NumByteToRead == 1)
{
Timeout = 0xFFFF;
/* Send START condition */
I2Cx->CR1 |= CR1_START_Set;
/* Wait until SB flag is set: EV5 */
while ((I2Cx->SR1&0x0001) != 0x0001)
{
if (Timeout-- == 0)
return Error;
}
/* Send slave address */
/* Reset the address bit0 for read */
SlaveAddress |= OAR1_ADD0_Set;
Address = SlaveAddress;
/* Send the slave address */
I2Cx->DR = Address;
/* Wait until ADDR is set: EV6_3, then program ACK = 0, clear ADDR
and program the STOP just after ADDR is cleared. The EV6_3
software sequence must complete before the current byte end of transfer.*/
/* Wait until ADDR is set */
Timeout = 0xFFFF;
while ((I2Cx->SR1&0x0002) != 0x0002)
{
if (Timeout-- == 0)
return Error;
}
/* Clear ACK bit */
I2Cx->CR1 &= CR1_ACK_Reset;
/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
software sequence must complete before the current byte end of transfer */
__disable_irq();
/* Clear ADDR flag */
temp = I2Cx->SR2;
/* Program the STOP */
I2Cx->CR1 |= CR1_STOP_Set;
/* Re-enable IRQs */
__enable_irq();
/* Wait until a data is received in DR register (RXNE = 1) EV7 */
while ((I2Cx->SR1 & 0x00040) != 0x000040);
/* Read the data */
*pBuffer = I2Cx->DR;
/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
while ((I2Cx->CR1&0x200) == 0x200);
/* Enable Acknowledgement to be ready for another reception */
I2Cx->CR1 |= CR1_ACK_Set;
}
else if (NumByteToRead == 2)
{
/* Set POS bit */
I2Cx->CR1 |= CR1_POS_Set;
Timeout = 0xFFFF;
/* Send START condition */
I2Cx->CR1 |= CR1_START_Set;
/* Wait until SB flag is set: EV5 */
while ((I2Cx->SR1&0x0001) != 0x0001)
{
if (Timeout-- == 0)
return Error;
}
Timeout = 0xFFFF;
/* Send slave address */
/* Set the address bit0 for read */
SlaveAddress |= OAR1_ADD0_Set;
Address = SlaveAddress;
/* Send the slave address */
I2Cx->DR = Address;
/* Wait until ADDR is set: EV6 */
while ((I2Cx->SR1&0x0002) != 0x0002)
{
if (Timeout-- == 0)
return Error;
}
/* EV6_1: The acknowledge disable should be done just after EV6,
that is after ADDR is cleared, so disable all active IRQs around ADDR clearing and
ACK clearing */
__disable_irq();
/* Clear ADDR by reading SR2 register */
temp = I2Cx->SR2;
/* Clear ACK */
I2Cx->CR1 &= CR1_ACK_Reset;
/*Re-enable IRQs */
__enable_irq();
/* Wait until BTF is set */
while ((I2Cx->SR1 & 0x00004) != 0x000004);
/* Disable IRQs around STOP programming and data reading because of the limitation ?*/
__disable_irq();
/* Program the STOP */
I2C_GenerateSTOP(I2Cx, ENABLE);
/* Read first data */
*pBuffer = I2Cx->DR;
/* Re-enable IRQs */
__enable_irq();
/**/
pBuffer++;
/* Read second data */
*pBuffer = I2Cx->DR;
/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
while ((I2Cx->CR1&0x200) == 0x200);
/* Enable Acknowledgement to be ready for another reception */
I2Cx->CR1 |= CR1_ACK_Set;
/* Clear POS bit */
I2Cx->CR1 &= CR1_POS_Reset;
}
else
{
Timeout = 0xFFFF;
/* Send START condition */
I2Cx->CR1 |= CR1_START_Set;
/* Wait until SB flag is set: EV5 */
while ((I2Cx->SR1&0x0001) != 0x0001)
{
if (Timeout-- == 0)
return Error;
}
Timeout = 0xFFFF;
/* Send slave address */
/* Reset the address bit0 for write */
SlaveAddress |= OAR1_ADD0_Set;;
Address = SlaveAddress;
/* Send the slave address */
I2Cx->DR = Address;
/* Wait until ADDR is set: EV6 */
while ((I2Cx->SR1&0x0002) != 0x0002)
{
if (Timeout-- == 0)
return Error;
}
/* Clear ADDR by reading SR2 status register */
temp = I2Cx->SR2;
/* While there is data to be read */
while (NumByteToRead)
{
/* Receive bytes from first byte until byte N-3 */
if (NumByteToRead != 3)
{
/* Poll on BTF to receive data because in polling mode we can not guarantee the
EV7 software sequence is managed before the current byte transfer completes */
while ((I2Cx->SR1 & 0x00004) != 0x000004);
/* Read data */
*pBuffer = I2Cx->DR;
/* */
pBuffer++;
/* Decrement the read bytes counter */
NumByteToRead--;
}
/* it remains to read three data: data N-2, data N-1, Data N */
if (NumByteToRead == 3)
{
/* Wait until BTF is set: Data N-2 in DR and data N -1 in shift register */
while ((I2Cx->SR1 & 0x00004) != 0x000004);
/* Clear ACK */
I2Cx->CR1 &= CR1_ACK_Reset;
/* Disable IRQs around data reading and STOP programming because of the
limitation ? */
__disable_irq();
/* Read Data N-2 */
*pBuffer = I2Cx->DR;
/* Increment */
pBuffer++;
/* Program the STOP */
I2Cx->CR1 |= CR1_STOP_Set;
/* Read DataN-1 */
*pBuffer = I2Cx->DR;
/* Re-enable IRQs */
__enable_irq();
/* Increment */
pBuffer++;
/* Wait until RXNE is set (DR contains the last data) */
while ((I2Cx->SR1 & 0x00040) != 0x000040);
/* Read DataN */
*pBuffer = I2Cx->DR;
/* Reset the number of bytes to be read by master */
NumByteToRead = 0;
}
}
/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
while ((I2Cx->CR1&0x200) == 0x200);
/* Enable Acknowledgement to be ready for another reception */
I2Cx->CR1 |= CR1_ACK_Set;
}
}
else /* I2Cx Master Reception using Interrupts with highest priority in an application */
{
/* Enable EVT IT*/
I2Cx->CR2 |= I2C_IT_EVT;
/* Enable BUF IT */
I2Cx->CR2 |= I2C_IT_BUF;
/* Set the I2C direction to reception */
I2CDirection = I2C_DIRECTION_RX;
SlaveAddress |= OAR1_ADD0_Set;
Address = SlaveAddress;
if (I2Cx == I2C1) NumbOfBytes1 = NumByteToRead;
else NumbOfBytes2 = NumByteToRead;
/* Send START condition */
I2Cx->CR1 |= CR1_START_Set;
/* Wait until the START condition is generated on the bus: START bit is cleared by hardware */
while ((I2Cx->CR1&0x100) == 0x100);
/* Wait until BUSY flag is reset (until a STOP is generated) */
while ((I2Cx->SR2 &0x0002) == 0x0002);
/* Enable Acknowledgement to be ready for another reception */
I2Cx->CR1 |= CR1_ACK_Set;
}
return Success;
}
/**
* @brief Writes buffer of bytes.
* @param pBuffer: Buffer of bytes to be sent to the slave.
* @param NumByteToWrite: Number of bytes to be sent by the Master.
* @param Mode: Polling or DMA or Interrupt having the highest priority in the application.
* @param SlaveAddress: The address of the slave to be addressed by the Master.
* @retval : None.
*/
Status I2C_Master_BufferWrite(I2C_TypeDef* I2Cx, uint8_t* pBuffer, uint32_t NumByteToWrite, I2C_ProgrammingModel Mode, uint8_t SlaveAddress )
{
__IO uint32_t temp = 0;
__IO uint32_t Timeout = 0;
/* Enable Error IT (used in all modes: DMA, Polling and Interrupts */
I2Cx->CR2 |= I2C_IT_ERR;
if (Mode == DMA) /* I2Cx Master Transmission using DMA */
{
Timeout = 0xFFFF;
/* Configure the DMA channel for I2Cx transmission */
I2C_DMAConfig (I2Cx, pBuffer, NumByteToWrite, I2C_DIRECTION_TX);
/* Enable the I2Cx DMA requests */
I2Cx->CR2 |= CR2_DMAEN_Set;
/* Send START condition */
I2Cx->CR1 |= CR1_START_Set;
/* Wait until SB flag is set: EV5 */
while ((I2Cx->SR1&0x0001) != 0x0001)
{
if (Timeout-- == 0)
return Error;
}
Timeout = 0xFFFF;
/* Send slave address */
/* Reset the address bit0 for write */
SlaveAddress &= OAR1_ADD0_Reset;
Address = SlaveAddress;
/* Send the slave address */
I2Cx->DR = Address;
/* Wait until ADDR is set: EV6 */
while ((I2Cx->SR1&0x0002) != 0x0002)
{
if (Timeout-- == 0)
return Error;
}
/* Clear ADDR flag by reading SR2 register */
temp = I2Cx->SR2;
if (I2Cx == I2C1)
{
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC6));
/* Disable the DMA1 Channel 6 */
DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE);
/* Clear the DMA Transfer complete flag */
DMA_ClearFlag(DMA1_FLAG_TC6);
}
else /* I2Cx = I2C2 */
{
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC4));
/* Disable the DMA1 Channel 4 */
DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE);
/* Clear the DMA Transfer complete flag */
DMA_ClearFlag(DMA1_FLAG_TC4);
}
/* EV8_2: Wait until BTF is set before programming the STOP */
while ((I2Cx->SR1 & 0x00004) != 0x000004);
/* Program the STOP */
I2Cx->CR1 |= CR1_STOP_Set;
/* Make sure that the STOP bit is cleared by Hardware */
while ((I2Cx->CR1&0x200) == 0x200);
}
else if (Mode == Polling) /* I2Cx Master Transmission using Polling */
{
Timeout = 0xFFFF;
/* Send START condition */
I2Cx->CR1 |= CR1_START_Set;
/* Wait until SB flag is set: EV5 */
while ((I2Cx->SR1&0x0001) != 0x0001)
{
if (Timeout-- == 0)
return Error;
}
/* Send slave address */
/* Reset the address bit0 for write*/
SlaveAddress &= OAR1_ADD0_Reset;
Address = SlaveAddress;
/* Send the slave address */
I2Cx->DR = Address;
Timeout = 0xFFFF;
/* Wait until ADDR is set: EV6 */
while ((I2Cx->SR1 &0x0002) != 0x0002)
{
if (Timeout-- == 0)
return Error;
}
/* Clear ADDR flag by reading SR2 register */
temp = I2Cx->SR2;
/* Write the first data in DR register (EV8_1) */
I2Cx->DR = *pBuffer;
/* Increment */
pBuffer++;
/* Decrement the number of bytes to be written */
NumByteToWrite--;
/* While there is data to be written */
while (NumByteToWrite--)
{
/* Poll on BTF to receive data because in polling mode we can not guarantee the
EV8 software sequence is managed before the current byte transfer completes */
while ((I2Cx->SR1 & 0x00004) != 0x000004);
/* Send the current byte */
I2Cx->DR = *pBuffer;
/* Point to the next byte to be written */
pBuffer++;
}
/* EV8_2: Wait until BTF is set before programming the STOP */
while ((I2Cx->SR1 & 0x00004) != 0x000004);
/* Send STOP condition */
I2Cx->CR1 |= CR1_STOP_Set;
/* Make sure that the STOP bit is cleared by Hardware */
while ((I2Cx->CR1&0x200) == 0x200);
}
else /* I2Cx Master Transmission using Interrupt with highest priority in the application */
{
/* Enable EVT IT*/
I2Cx->CR2 |= I2C_IT_EVT;
/* Enable BUF IT */
I2Cx->CR2 |= I2C_IT_BUF;
/* Set the I2C direction to Transmission */
I2CDirection = I2C_DIRECTION_TX;
SlaveAddress &= OAR1_ADD0_Reset;
Address = SlaveAddress;
if (I2Cx == I2C1) NumbOfBytes1 = NumByteToWrite;
else NumbOfBytes2 = NumByteToWrite;
/* Send START condition */
I2Cx->CR1 |= CR1_START_Set;
/* Wait until the START condition is generated on the bus: the START bit is cleared by hardware */
while ((I2Cx->CR1&0x100) == 0x100);
/* Wait until BUSY flag is reset: a STOP has been generated on the bus signaling the end
of transmission */
while ((I2Cx->SR2 &0x0002) == 0x0002);
}
return Success;
}
/**
* @brief Prepares the I2Cx slave for transmission.
* @param I2Cx: I2C1 or I2C2.
* @param Mode: DMA or Interrupt having the highest priority in the application.
* @retval : None.
*/
void I2C_Slave_BufferReadWrite(I2C_TypeDef* I2Cx,I2C_ProgrammingModel Mode)
{
/* Enable Event IT needed for ADDR and STOPF events ITs */
I2Cx->CR2 |= I2C_IT_EVT ;
/* Enable Error IT */
I2Cx->CR2 |= I2C_IT_ERR;
if (Mode == DMA) /* I2Cx Slave Transmission using DMA */
{
/* Enable I2Cx DMA requests */
I2Cx->CR2 |= CR2_DMAEN_Set;
}
else /* I2Cx Slave Transmission using Interrupt with highest priority in the application */
{
/* Enable Buffer IT (TXE and RXNE ITs) */
I2Cx->CR2 |= I2C_IT_BUF;
}
}
/**
* @brief Initializes peripherals: I2Cx, GPIO, DMA channels .
* @param None
* @retval None
*/
void I2C_LowLevel_Init(I2C_TypeDef* I2Cx)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
/* GPIOB clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Enable the DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
if (I2Cx == I2C1)
{
/* I2C1 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
/* I2C1 SDA and SCL configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Enable I2C1 reset state */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
/* Release I2C1 from reset state */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
}
else /* I2Cx = I2C2 */
{
/* I2C2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
/* I2C1 SDA and SCL configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Enable I2C2 reset state */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, ENABLE);
/* Release I2C2 from reset state */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, DISABLE);
}
/* I2C1 and I2C2 configuration */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = OwnAddress1;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = ClockSpeed;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_InitStructure.I2C_OwnAddress1 = OwnAddress2;
I2C_Init(I2C2, &I2C_InitStructure);
if (I2Cx == I2C1)
{ /* I2C1 TX DMA Channel configuration */
DMA_DeInit(I2C1_DMA_CHANNEL_TX);
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_BufferSize = 0xFFFF; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
I2CDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
I2CDMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte;
I2CDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
I2CDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
I2CDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
I2CDMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
/* I2C1 RX DMA Channel configuration */
DMA_DeInit(I2C1_DMA_CHANNEL_RX);
DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
}
else /* I2Cx = I2C2 */
{
/* I2C2 TX DMA Channel configuration */
DMA_DeInit(I2C2_DMA_CHANNEL_TX);
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;
I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_BufferSize = 0xFFFF; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
I2CDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
I2CDMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte;
I2CDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
I2CDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
I2CDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
I2CDMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(I2C2_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
/* I2C2 RX DMA Channel configuration */
DMA_DeInit(I2C2_DMA_CHANNEL_RX);
DMA_Init(I2C2_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
}
}
/**
* @brief Initializes DMA channel used by the I2C Write/read routines.
* @param None.
* @retval None.
*/
void I2C_DMAConfig(I2C_TypeDef* I2Cx, uint8_t* pBuffer, uint32_t BufferSize, uint32_t Direction)
{
/* Initialize the DMA with the new parameters */
if (Direction == I2C_DIRECTION_TX)
{
/* Configure the DMA Tx Channel with the buffer address and the buffer size */
I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pBuffer;
I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
I2CDMA_InitStructure.DMA_BufferSize = (uint32_t)BufferSize;
if (I2Cx == I2C1)
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE);
DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
DMA_Cmd(I2C1_DMA_CHANNEL_TX, ENABLE);
}
else
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;
DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE);
DMA_Init(I2C2_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
DMA_Cmd(I2C2_DMA_CHANNEL_TX, ENABLE);
}
}
else /* Reception */
{
/* Configure the DMA Rx Channel with the buffer address and the buffer size */
I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pBuffer;
I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
I2CDMA_InitStructure.DMA_BufferSize = (uint32_t)BufferSize;
if (I2Cx == I2C1)
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
DMA_Cmd(I2C1_DMA_CHANNEL_RX, DISABLE);
DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
DMA_Cmd(I2C1_DMA_CHANNEL_RX, ENABLE);
}
else
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;
DMA_Cmd(I2C2_DMA_CHANNEL_RX, DISABLE);
DMA_Init(I2C2_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
DMA_Cmd(I2C2_DMA_CHANNEL_RX, ENABLE);
}
}
}
/**
* @}
*/
/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/
@file OptimizedI2Cexamples/src/stm32f10x_it.c 中断函数文件
/******************************************************************************/
/* STM32F10x Peripherals Interrupt Handlers */
/* Add here the Interrupt Handler for the used peripheral(s) (I2C), for the */
/* available peripheral interrupt handler's name please refer to the startup */
/* file (startup_stm32f10x_xx.s). */
/******************************************************************************/
/**
* @brief This function handles I2C1 Event interrupt request.
* @param None
* @retval : None
*/
void I2C1_EV_IRQHandler(void)
{
__IO uint32_t SR1Register =0;
__IO uint32_t SR2Register =0;
#ifdef SLAVE_DMA_USE
/* Read SR1 register */
SR1Register = I2C1->SR1;
/* If ADDR is set 若从机检测到地址匹配 */
if ((SR1Register & 0x0002) == 0x0002)
{
/* In slave Transmitter/Receiver mode, when using DMA, it is recommended to update the buffer
base address and the buffer size before clearing ADDR flag. In fact, the only
period when the slave has control on the bus(SCL is stretched so master can not initiate
transfers) is the period between ADDR is set and ADDR is cleared. Otherwise, the master can
initiate transfers and the buffer size & the buffer address have not yet been updated.*/
/* Update the DMA channels memory base address and count */
I2C_DMAConfig (I2C1, Buffer_Tx1, 0xFFFF, I2C_DIRECTION_TX);
I2C_DMAConfig (I2C1, Buffer_Rx1, 0xFFFF, I2C_DIRECTION_RX);
/* Clear ADDR by reading SR2 register */
SR2Register = I2C1->SR2;
}
#else
/* Read the I2C1 SR1 and SR2 status registers */
SR1Register = I2C1->SR1;
SR2Register = I2C1->SR2;
/* 若IIC控制器是从机 */
if ((SR2Register &0x0001) != 0x0001) //=0从机模式
{
/* If ADDR = 1: EV1 */
if ((SR1Register & 0x0002) == 0x0002) //若地址匹配
{
/* 清除 SR1 SR2 的状态标记位,为下一次中断做准备 */
SR1Register = 0;
SR2Register = 0;
/* Initialize the transmit/receive counters for next transmission/reception
using Interrupt */
Tx_Idx1 = 0;
Rx_Idx1 = 0;
}
/* If TXE = 1: EV3 */
if ((SR1Register & 0x0080) == 0x0080)
{
/* 向数据寄存器中写入数据 */
I2C1->DR = Buffer_Tx1[Tx_Idx1++];
SR1Register = 0;
SR2Register = 0;
}
/* If RXNE = 1: EV2 */
if ((SR1Register & 0x0040) == 0x0040)
{
/* 从数据寄存器中读取数据 */
Buffer_Rx1[Rx_Idx1++] = I2C1->DR;
SR1Register = 0;
SR2Register = 0;
}
/* If STOPF =1: EV4 从机检测到总线上的通信结束信号 */
if (( SR1Register & 0x0010) == 0x0010)
{
I2C1->CR1 |= CR1_PE_Set;
SR1Register = 0;
SR2Register = 0;
}
} /* End slave mode */
#endif
/* If SB = 1, I2C1 主机在总线上发送开始通信信号: EV5) */
if ((SR1Register &0x0001) == 0x0001)//若主机已发送开始通信信号
{
/* Send the slave address for transmssion or for reception (according to the configured value
in the write master write routine */
//发送地址数据,发送模式下,写入寄存器即发送,硬件自动发送。
I2C1->DR = Address;
SR1Register = 0;
SR2Register = 0;
}
/* 若IIC控制器是主机 */
if ((SR2Register &0x0001) == 0x0001) //=1主机模式
{
/* If ADDR = 1, EV6 若地址数据发送完成 */
if ((SR1Register &0x0002) == 0x0002)
{
/* Write the first data in case the Master is Transmitter */
if (I2CDirection == I2C_DIRECTION_TX)
{
/* Initialize the Transmit counter */
Tx_Idx1 = 0;
/* Write the first data in the data register */
I2C1->DR = Buffer_Tx1[Tx_Idx1++];
/* Decrement the number of bytes to be written */
NumbOfBytes1--;
/* If no further data to be sent, disable the I2C BUF IT
in order to not have a TxE interrupt */
if (NumbOfBytes1 == 0)
{
I2C1->CR2 &= (uint16_t)~I2C_IT_BUF;
}
}
/* Master Receiver */
else
{
/* Initialize Receive counter */
Rx_Idx1 = 0;
/* At this stage, ADDR is cleared because both SR1 and SR2 were read.*/
/* EV6_1: used for single byte reception. The ACK disable and the STOP
Programming should be done just after ADDR is cleared. */
if (NumbOfBytes1 == 1)
{
/* Clear ACK */
I2C1->CR1 &= CR1_ACK_Reset;
/* Program the STOP */
I2C1->CR1 |= CR1_STOP_Set;
}
}
SR1Register = 0;
SR2Register = 0;
}
/* Master transmits the remaing data: from data2 until the last one. */
/* If TXE is set */
if ((SR1Register &0x0084) == 0x0080)
{
/* If there is still data to write */
if (NumbOfBytes1!=0)
{
/* Write the data in DR register */
I2C1->DR = Buffer_Tx1[Tx_Idx1++];
/* Decrment the number of data to be written */
NumbOfBytes1--;
/* If no data remains to write, disable the BUF IT in order
to not have again a TxE interrupt. */
if (NumbOfBytes1 == 0)
{
/* Disable the BUF IT */
I2C1->CR2 &= (uint16_t)~I2C_IT_BUF;
}
}
SR1Register = 0;
SR2Register = 0;
}
/* If BTF and TXE are set (EV8_2), program the STOP */
if ((SR1Register &0x0084) == 0x0084)
{
/* Program the STOP */
I2C1->CR1 |= CR1_STOP_Set;
/* Disable EVT IT In order to not have again a BTF IT */
I2C1->CR2 &= (uint16_t)~I2C_IT_EVT;
SR1Register = 0;
SR2Register = 0;
}
/* If RXNE is set */
if ((SR1Register &0x0040) == 0x0040)
{
/* Read the data register */
Buffer_Rx1[Rx_Idx1++] = I2C1->DR;
/* Decrement the number of bytes to be read */
NumbOfBytes1--;
/* If it remains only one byte to read, disable ACK and program the STOP (EV7_1) */
if (NumbOfBytes1 == 1)
{
/* Clear ACK */
I2C1->CR1 &= CR1_ACK_Reset;
/* Program the STOP */
I2C1->CR1 |= CR1_STOP_Set;
}
SR1Register = 0;
SR2Register = 0;
}
}
}
/**
* @}
*/
/**
* @brief This function handles I2C1 Event interrupt request.
* @param None
* @retval : None
*/
void I2C2_EV_IRQHandler(void)
{
__IO uint32_t SR1Register =0;
__IO uint32_t SR2Register =0;
#ifdef SLAVE_DMA_USE
/* Read SR1 register */
SR1Register = I2C2->SR1;
/* If ADDR is set */
if ((SR1Register & 0x0002) == 0x0002)
{
/* In slave Transmitter/Receiver mode, when using DMA, the update of the buffer base address
and the buffer size should be done before clearing ADDR flag. In fact, the only
period when the slave has control on the bus(SCL is stretched so master can not initiate
transfers) is the period between ADDR is set and ADDR is cleared. otherwise, the master can
initiate transfers and the buffer size & the buffer address have not yet updated.*/
/* Update the DMA channels memory base address and count */
I2C_DMAConfig (I2C2, Buffer_Tx2, 0xFFFF, I2C_DIRECTION_TX);
I2C_DMAConfig (I2C2, Buffer_Rx2, 0xFFFF, I2C_DIRECTION_RX);
/* Clear ADDR by reading SR2 register */
SR2Register = I2C2->SR2;
}
#else
/* Read the I2C1 SR1 and SR2 status registers */
SR1Register = I2C2->SR1;
SR2Register = I2C2->SR2;
/* If I2C2 is slave (MSL flag = 0) */
if ((SR2Register &0x0001) != 0x0001)
{
/* If ADDR = 1: EV1 */
if ((SR1Register & 0x0002) == 0x0002)
{
/* Clear SR1Register SR2Register variables to prepare for next IT*/
SR1Register = 0;
SR2Register = 0;
/* Initialize the transmit/receive counters for next transmission/reception
using Interrupt */
Tx_Idx2 = 0;
Rx_Idx2 = 0;
}
/* If TXE = 1: EV3 */
if ((SR1Register & 0x0080) == 0x0080)
{
/* Write data in data register */
I2C2->DR = Buffer_Tx2[Tx_Idx2++];
SR1Register = 0;
SR2Register = 0;
}
/* If RXNE = 1: EV2 */
if ((SR1Register & 0x0040) == 0x0040)
{
/* Read data from data register */
Buffer_Rx2[Rx_Idx2++] = I2C2->DR;
SR1Register = 0;
SR2Register = 0;
}
/* If STOPF =1: EV4 (Slave has detected a STOP condition on the bus */
if (( SR1Register & 0x0010) == 0x0010)
{
I2C2->CR1 |= CR1_PE_Set;
SR1Register = 0;
SR2Register = 0;
}
} /* End slave mode */
#endif
/* If SB = 1, I2C1 master sent a START on the bus: EV5) */
if ((SR1Register &0x0001) == 0x0001)
{
/* Send the slave address for transmssion or for reception (according to the configured value
in the write master write routine */
I2C2->DR = Address;
SR1Register = 0;
SR2Register = 0;
}
/* If I2C2 is Master (MSL flag = 1) */
if ((SR2Register &0x0001) == 0x0001)
{
/* If ADDR = 1, EV6 */
if ((SR1Register &0x0002) == 0x0002)
{
/* Write the first data in case the Master is Transmitter */
if (I2CDirection == I2C_DIRECTION_TX)
{
/* Initialize the Transmit counter */
Tx_Idx2 = 0;
/* Write the first data in the data register */
I2C2->DR = Buffer_Tx1[Tx_Idx2++];
/* Decrement the number of bytes to be written */
NumbOfBytes2--;
/* If no further data to be sent, disable the I2C BUF IT
in order to not have a TxE interrupt */
if (NumbOfBytes2 == 0)
{
I2C2->CR2 &= (uint16_t)~I2C_IT_BUF;
}
}
/* Master Receiver */
else
{
/* Initialize Receive counter */
Rx_Idx2 = 0;
/* At this stage, ADDR is cleared because both SR1 and SR2 were read.*/
/* EV6_1: used for single byte reception. The ACK disable and the STOP
Programming should be done just after ADDR is cleared. */
if (NumbOfBytes2 == 1)
{
/* Clear ACK */
I2C2->CR1 &= CR1_ACK_Reset;
/* Program the STOP */
I2C2->CR1 |= CR1_STOP_Set;
}
}
SR1Register = 0;
SR2Register = 0;
}
/* Master transmits the remaing data: from data2 until the last one. */
/* If TXE is set */
if ((SR1Register &0x0084) == 0x0080)
{
/* If there is still data to write */
if (NumbOfBytes2!=0)
{
/* Write the data in DR register */
I2C2->DR = Buffer_Tx2[Tx_Idx2++];
/* Decrment the number of data to be written */
NumbOfBytes2--;
/* If no data remains to write, disable the BUF IT in order
to not have again a TxE interrupt. */
if (NumbOfBytes2 == 0)
{
/* Disable the BUF IT */
I2C2->CR2 &= (uint16_t)~I2C_IT_BUF;
}
}
SR1Register = 0;
SR2Register = 0;
}
/* If BTF and TXE are set (EV8_2), program the STOP */
if ((SR1Register &0x0084) == 0x0084)
{
/* Program the STOP */
I2C2->CR1 |= CR1_STOP_Set;
/* Disable EVT IT In order to not have again a BTF IT */
I2C2->CR2 &= (uint16_t)~I2C_IT_EVT;
SR1Register = 0;
SR2Register = 0;
}
/* If RXNE is set */
if ((SR1Register &0x0040) == 0x0040)
{
/* Read the data register */
Buffer_Rx2[Rx_Idx2++] = I2C2->DR;
/* Decrement the number of bytes to be read */
NumbOfBytes2--;
/* If it remains only one byte to read, disable ACK and program the STOP (EV7_1) */
if (NumbOfBytes2 == 1)
{
/* Clear ACK */
I2C2->CR1 &= CR1_ACK_Reset;
/* Program the STOP */
I2C2->CR1 |= CR1_STOP_Set;
}
SR1Register = 0;
SR2Register = 0;
}
}
}
/**
* @}
*/
/**
* @brief This function handles I2C2 Error interrupt request.
* @param None
* @retval : None
*/
void I2C2_ER_IRQHandler(void)
{
__IO uint32_t SR1Register =0;
/* Read the I2C1 status register */
SR1Register = I2C2->SR1;
/* If AF = 1 */
if ((SR1Register & 0x0400) == 0x0400)
{
I2C2->SR1 &= 0xFBFF;
SR1Register = 0;
}
/* If ARLO = 1 */
if ((SR1Register & 0x0200) == 0x0200)
{
I2C2->SR1 &= 0xFBFF;
SR1Register = 0;
}
/* If BERR = 1 */
if ((SR1Register & 0x0100) == 0x0100)
{
I2C2->SR1 &= 0xFEFF;
SR1Register = 0;
}
/* If OVR = 1 */
if ((SR1Register & 0x0800) == 0x0800)
{
I2C2->SR1 &= 0xF7FF;
SR1Register = 0;
}
}
/**
* @brief This function handles I2C1 Error interrupt request.
* @param None
* @retval : None
*/
void I2C1_ER_IRQHandler(void)
{
__IO uint32_t SR1Register =0;
/* Read the I2C1 status register */
SR1Register = I2C1->SR1;
/* If AF = 1 */
if ((SR1Register & 0x0400) == 0x0400)
{
I2C1->SR1 &= 0xFBFF;
SR1Register = 0;
}
/* If ARLO = 1 */
if ((SR1Register & 0x0200) == 0x0200)
{
I2C1->SR1 &= 0xFBFF;
SR1Register = 0;
}
/* If BERR = 1 */
if ((SR1Register & 0x0100) == 0x0100)
{
I2C1->SR1 &= 0xFEFF;
SR1Register = 0;
}
/* If OVR = 1 */
if ((SR1Register & 0x0800) == 0x0800)
{
I2C1->SR1 &= 0xF7FF;
SR1Register = 0;
}
}
3. 源代码-HAL版本
这里只列举部分代码,不涉及应用,只涉及IIC的硬件抽象层HAL
/**
******************************************************************************
* @file stm32f1xx_hal_i2c.c
* @author MCD Application Team
* @brief I2C HAL module driver.
* This file provides firmware functions to manage the following
* functionalities of the Inter Integrated Circuit (I2C) peripheral:
* + Initialization and de-initialization functions
* + IO operation functions
* + Peripheral State, Mode and Error functions
*
@verbatim
==============================================================================
##### How to use this driver #####
==============================================================================
[..]
The I2C HAL driver can be used as follows:
(#) Declare a I2C_HandleTypeDef handle structure, for example:
I2C_HandleTypeDef hi2c;
(#)Initialize the I2C low level resources by implementing the @ref HAL_I2C_MspInit() API:
(##) Enable the I2Cx interface clock
(##) I2C pins configuration
(+++) Enable the clock for the I2C GPIOs
(+++) Configure I2C pins as alternate function open-drain
(##) NVIC configuration if you need to use interrupt process
(+++) Configure the I2Cx interrupt priority
(+++) Enable the NVIC I2C IRQ Channel
(##) DMA Configuration if you need to use DMA process
(+++) Declare a DMA_HandleTypeDef handle structure for the transmit or receive channel
(+++) Enable the DMAx interface clock using
(+++) Configure the DMA handle parameters
(+++) Configure the DMA Tx or Rx channel
(+++) Associate the initialized DMA handle to the hi2c DMA Tx or Rx handle
(+++) Configure the priority and enable the NVIC for the transfer complete interrupt on
the DMA Tx or Rx channel
(#) Configure the Communication Speed, Duty cycle, Addressing mode, Own Address1,
Dual Addressing mode, Own Address2, General call and Nostretch mode in the hi2c Init structure.
(#) Initialize the I2C registers by calling the @ref HAL_I2C_Init(), configures also the low level Hardware
(GPIO, CLOCK, NVIC...etc) by calling the customized @ref HAL_I2C_MspInit() API.
(#) To check if target device is ready for communication, use the function @ref HAL_I2C_IsDeviceReady()
(#) For I2C IO and IO MEM operations, three operation modes are available within this driver :
*** Polling mode IO operation ***
=================================
[..]
(+) Transmit in master mode an amount of data in blocking mode using @ref HAL_I2C_Master_Transmit()
(+) Receive in master mode an amount of data in blocking mode using @ref HAL_I2C_Master_Receive()
(+) Transmit in slave mode an amount of data in blocking mode using @ref HAL_I2C_Slave_Transmit()
(+) Receive in slave mode an amount of data in blocking mode using @ref HAL_I2C_Slave_Receive()
*** Polling mode IO MEM operation ***
=====================================
[..]
(+) Write an amount of data in blocking mode to a specific memory address using @ref HAL_I2C_Mem_Write()
(+) Read an amount of data in blocking mode from a specific memory address using @ref HAL_I2C_Mem_Read()
*** Interrupt mode IO operation ***
===================================
[..]
(+) Transmit in master mode an amount of data in non-blocking mode using @ref HAL_I2C_Master_Transmit_IT()
(+) At transmission end of transfer, @ref HAL_I2C_MasterTxCpltCallback() is executed and user can
add his own code by customization of function pointer @ref HAL_I2C_MasterTxCpltCallback()
(+) Receive in master mode an amount of data in non-blocking mode using @ref HAL_I2C_Master_Receive_IT()
(+) At reception end of transfer, @ref HAL_I2C_MasterRxCpltCallback() is executed and user can
add his own code by customization of function pointer @ref HAL_I2C_MasterRxCpltCallback()
(+) Transmit in slave mode an amount of data in non-blocking mode using @ref HAL_I2C_Slave_Transmit_IT()
(+) At transmission end of transfer, @ref HAL_I2C_SlaveTxCpltCallback() is executed and user can
add his own code by customization of function pointer @ref HAL_I2C_SlaveTxCpltCallback()
(+) Receive in slave mode an amount of data in non-blocking mode using @ref HAL_I2C_Slave_Receive_IT()
(+) At reception end of transfer, @ref HAL_I2C_SlaveRxCpltCallback() is executed and user can
add his own code by customization of function pointer @ref HAL_I2C_SlaveRxCpltCallback()
(+) In case of transfer Error, @ref HAL_I2C_ErrorCallback() function is executed and user can
add his own code by customization of function pointer @ref HAL_I2C_ErrorCallback()
(+) Abort a master I2C process communication with Interrupt using @ref HAL_I2C_Master_Abort_IT()
(+) End of abort process, @ref HAL_I2C_AbortCpltCallback() is executed and user can
add his own code by customization of function pointer @ref HAL_I2C_AbortCpltCallback()
*** Interrupt mode or DMA mode IO sequential operation ***
==========================================================
[..]
(@) These interfaces allow to manage a sequential transfer with a repeated start condition
when a direction change during transfer
[..]
(+) A specific option field manage the different steps of a sequential transfer
(+) Option field values are defined through @ref I2C_XferOptions_definition and are listed below:
(++) I2C_FIRST_AND_LAST_FRAME: No sequential usage, functionnal is same as associated interfaces in no sequential mode
(++) I2C_FIRST_FRAME: Sequential usage, this option allow to manage a sequence with start condition, address
and data to transfer without a final stop condition
(++) I2C_FIRST_AND_NEXT_FRAME: Sequential usage (Master only), this option allow to manage a sequence with start condition, address
and data to transfer without a final stop condition, an then permit a call the same master sequential interface
several times (like @ref HAL_I2C_Master_Seq_Transmit_IT() then @ref HAL_I2C_Master_Seq_Transmit_IT()
or @ref HAL_I2C_Master_Seq_Transmit_DMA() then @ref HAL_I2C_Master_Seq_Transmit_DMA())
(++) I2C_NEXT_FRAME: Sequential usage, this option allow to manage a sequence with a restart condition, address
and with new data to transfer if the direction change or manage only the new data to transfer
if no direction change and without a final stop condition in both cases
(++) I2C_LAST_FRAME: Sequential usage, this option allow to manage a sequance with a restart condition, address
and with new data to transfer if the direction change or manage only the new data to transfer
if no direction change and with a final stop condition in both cases
(++) I2C_LAST_FRAME_NO_STOP: Sequential usage (Master only), this option allow to manage a restart condition after several call of the same master sequential
interface several times (link with option I2C_FIRST_AND_NEXT_FRAME).
Usage can, transfer several bytes one by one using HAL_I2C_Master_Seq_Transmit_IT(option I2C_FIRST_AND_NEXT_FRAME then I2C_NEXT_FRAME)
or HAL_I2C_Master_Seq_Receive_IT(option I2C_FIRST_AND_NEXT_FRAME then I2C_NEXT_FRAME)
or HAL_I2C_Master_Seq_Transmit_DMA(option I2C_FIRST_AND_NEXT_FRAME then I2C_NEXT_FRAME)
or HAL_I2C_Master_Seq_Receive_DMA(option I2C_FIRST_AND_NEXT_FRAME then I2C_NEXT_FRAME).
Then usage of this option I2C_LAST_FRAME_NO_STOP at the last Transmit or Receive sequence permit to call the oposite interface Receive or Transmit
without stopping t