使用STM8L的硬件I2C(三)硬件I2C的中断读写处理

使用STM8L的硬件I2C(三)硬件I2C的中断读写处理

其他系列文章参见
(一)硬件I2C的简介
(二)硬件I2C的事件和检测
(四)硬件I2C的使用注意

1、I2C初始化

关于I2C初始化的流程,标准库中的注释写的非常清晰:

  *          1. Enable peripheral clock using CLK_PeripheralClockConfig(CLK_Peripheral_I2Cx,
  *             ENABLE) function (Refer to the product datasheet for the available I2C
  *           	peripherals)
  *
  *          2. Program the Mode, duty cycle , Own address, Ack, Speed and Acknowledged
  *             Address using the I2C_Init() function.
  *
  *          3. Optionally you can enable/configure the following parameters without
  *             re-initialization (i.e there is no need to call again I2C_Init() function):
  *              - Enable the acknowledge feature using I2C_AcknowledgeConfig() function
  *              - Enable the dual addressing mode using I2C_DualAddressCmd() function
  *              - Enable the general call using the I2C_GeneralCallCmd() function
  *              - Enable the clock stretching using I2C_StretchClockCmd() function
  *              - Enable the fast mode duty cycle using the I2C_FastModeDutyCycleConfig()
  *                function
  *              - Enable the PEC Calculation using I2C_CalculatePEC() function
  *              - For SMBus Mode:
  *                   - Enable the Address Resolution Protocol (ARP) using I2C_ARPCmd() function
  *                   - Configure the SMBusAlert pin using I2C_SMBusAlertConfig() function
  *
  *          4. Enable the interrupt using the function I2C_ITConfig() if you need
  *             to use interrupt mode.
  *
  *          5. When using the DMA mode
  *                   - Configure the DMA using DMA_Init() function
  *                   - Active the needed channel Request using I2C_DMACmd() or
  *                     I2C_DMALastTransferCmd() function
  *              Note: When using DMA mode, I2C interrupts may be used at the same time to
  *                    control the communication flow (Start/Stop/Ack... events and errors).
  *
  *          6. Enable the I2C using the I2C_Cmd() function.
  *
  *          7. Enable the DMA using the DMA_Cmd() function when using DMA mode in the
  *             transfers.
  *
  *         Note: The external Pull-up resistors must be connected on SDA and SCL.
  *

流程很标准,翻译如下:

  1. 使能I2C时钟 (CLK_PeripheralClockConfig函数)
  2. 初始化总线参数 (I2C_Init函数)
    通信频率、I2C模式(Master/Slave/DMA)、Duty、Ack自动、地址类型(7/10)
  3. 用其他设定函数重新设置或者enable选项(无需再次调用I2C_init()函数)
  4. 如果使用中断模式,开中断(I2C_ITConfig函数)
  5. 如果使用DMA,做DMA相关设置(DMA_Init/I2C_DMALastTransferCmd))
  6. 使能I2C(I2C_Cmd函数)
    注意必须要要先使能I2C时钟,否则后续设定无效,直接会导致通信不正常

2、STM8L I2C的中断类型

I2C的硬件中断有3中类型,分别为Event、Buffer、Error。
Event是I2C协议自身状态的中断,如Start、Stop的发送、地址发送完成、数据发送、接收完成等。
Buffer实际上要依附于Event中断,提供缓冲区相关的中断。比如缓冲(数据寄存器)可写、缓冲(数据寄存器)可读等。
Error顾名思义,是错误相关的中断。
具体表如下:
I2C中断

3、I2C Master的Read与Write

I2C外设都走I2C协议,但是在协议设计上却不尽相同。

一般来说,用I2C这种协议的外设相对复杂,自身也会进行一些设定,
从而自身内部会存在多个寄存器以区分不同的设定以及数据存储,以便和外界沟通。
与这种I2C外设交互时,往往不能只发送外设地址,还需要指定目标寄存器。
而I2C协议上只规定发送外设地址,因此寄存器地址只能作为应用层数据承载。
(有些I2C设备很简单,不需要指定寄存器,只需指定外设地址就可以。比如BH1750)

对于Write来说,外设寄存器地址也是数据,
寄存器地址本身和要写入寄存器的数据并没有区别,
只要串行写入多个数据(寄存器地址+真正数据)就可以正常工作。

而对于Read来说,有所不同。
因为I2C只能指定外设地址,那目标寄存器的指定自然存在一个问题:
必须先告知I2C外设,要读的目标寄存器的地址是什么,而告知本身的实现需要一个写操作。
也就是说,作为Read,要先写数据给外设(告知寄存器地址),然后再真正去读。

但是这个读之中的先写和后读如何实施,不同的I2C外设处理也不尽相同,
需要参考外设的手册,因为这实际上是一个应用层协议的设计问题。
因此,通常情况下对于大多数I2C外设,它的Read流程会比Write流程操作要多一步,复杂一些。

以常用的温度压力传感器博世BMP280为例,它的I2C Read流程如下:

  1. 先对外设地址做Write,发送要读取的BMP280寄存器地址
  2. 发送ReStart表示写结束
  3. 紧跟Restart发起对外设地址的Read(寄存器地址还要再送一遍)
    可以看出,它的先写后读之间是用一个RepeatStart/ReStart做衔接区分的。
    (根据BMP280手册,这个衔接可以用Stop或者Restart,但是不能用Stop+Start)
    BMP280多字节读
    而它的Write,如下图:就简单的多,把寄存器地址和数据直接串行写就可以。
    BMP280写

3.1 I2C Master Write

还是以BMP280为例,介绍使用中断方式进行读写流程的处理。
写流程很简单:

先回顾一下标准库的抽象出的I2C写流程:
Master写
再来看具体的实现流程:

写函数:

  1. 开中断
  2. 开Ack自动回应
  3. 生成Start
  4. 循环等待完成标志(同步写)(异步写可以在主循环中判断完成标志或者直接callback)

中断处理:

  1. 中断到来,用I2C_GetLastEvent()获取当前总线状态

  2. 根据总线状态,执行写地址、写数据、生成Stop、关中断的各种操作,具体如下:

    EV5:送Slave地址(Write)
    EV6/EV8:每次发送1字节数据,发送完最后一个字节关中断(I2C_IT_BUF)
    EV8_2:发送Stop,关中断(I2C_IT_ERR和I2C_IT_EVT)
    
  3. 置标志位(表示写完成)

3.2 I2C Master Read

相对而言,Read要比Write复杂多了。
一方面是因为上面提到的外设寄存器地址指定的问题,另一方面更复杂的是NAck和Stop的发送规则。
后者是模拟方式根本不用考虑的的问题。
但是作为硬件处理,又加上中断,处理速度快,Nack和Stop的发送时机变得非常重要,该发Nack的时候
发送了Ack会导致整个流程都不正常。STD标准库为说明这个问题,简直是长篇大论。
基本上分为:1字节读取、2字节读取,处理方式各不相同,且中断模式和Polling也不相同。

先回顾一下标准库的抽象出的I2C读流程:
Master 接收
再来看具体的实现流程:(Nack和Stop设置的问题另外再说明)

读函数:

  1. 开中断
  2. 开Ack自动回应
  3. 生成Start
  4. 循环等待完成标志(同步读)(异步读可以在主循环中判断完成标志或者直接callback)

中断处理:

  1. 中断到来,用I2C_GetLastEvent()获取当前总线状态

  2. 根据总线状态,执行写地址、读数据、生成Ack/Nack/Stop的各种操作,具体如下

    // 先写部分(送寄存器地址):
    	EV5:送Slave地址(Write)
    	EV6/EV8:写寄存器地址(作为数据)
    	EV8_2:发送ReStart(即没有发送STOP的情况下再发一次Start)
    
    // 后读部分(读寄存器地址):
    	EV5:送Slave地址(Read)
    	EV6:如果仅需读取字节数n=1,则直接关Ack并发送Stop,n>=2,则不做动作
    	EV7:先读数据,然后判断剩余字节n,如果n=1,则关Ack并发送Stop,如n=0,则关中断
    
  3. 数据写完成后,置标志位(表示写完成)
    (Ack设置为自动回复,所以无需显示执行发送Ack)

需要注意:EV6实际有2个定义,分别代表Master写就绪和Master读就绪,
都称为EV6,但实际不会冲突,因为它们是出现在不同的模式种。
EV6(Write):I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED
EV6(Read):I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED

4、代码示例

以下代码在BMP280+STM8L152C6T6上运行正常。

4.1 I2C读写缓冲区和状态定义

#define BUFFERSIZE  		(32) //BMP280最多一次性会读取24字节,所以缓冲区要>=24字节
#pragma pack(1)
struct i2c_dev {
	uint8_t addr;
	uint8_t state;
	uint8_t tx_idx;
	uint8_t rx_idx;
	uint8_t tx_len;
	uint8_t rx_len;
	uint8_t TxBuffer[BUFFERSIZE];
	uint8_t RxBuffer[BUFFERSIZE];
};
#pragma pack()

enum {
	BMP280_I2C_RESET = 0x00,
	BMP280_I2C_WRITE_REG,
	BMP280_I2C_WRITE_REG_END,
	BMP280_I2C_READ_REG1,
    BMP280_I2C_READ_REG2,
	BMP280_I2C_READ_REG_END
};

4.1 I2C初始化

int8_t i2c_init(void)
{
	/* I2C clock Enable*/
	CLK_PeripheralClockConfig(CLK_Peripheral_I2C1, ENABLE);
	
	I2C_DeInit(I2C1);

	/* Initialize I2C peripheral */
	I2C_Init(I2C1, 100000, 0xA1,
	         I2C_Mode_I2C, I2C_DutyCycle_2,
	         I2C_Ack_Enable, I2C_AcknowledgedAddress_7bit);
	
	/* Enable Buffer and Event Interrupt*/
	I2C_ITConfig(I2C1, (I2C_IT_TypeDef)(I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF) , ENABLE);

	/* Enable I2C */
	I2C_Cmd(I2C1, ENABLE);
}

4.2 I2C写函数

int8_t i2c_reg_write(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length)
{
	  uint8_t count = 0;
		
		// 初始化
		reset_dev(&_i2c_dev);
		
   		// 指定Slave目标地址
		_i2c_dev.addr = i2c_addr;
		_i2c_dev.state = BMP280_I2C_WRITE_REG;
		
		// Write数据从用户缓冲复制到I2C缓冲
		// 第一字节数据是寄存器地址
		_i2c_dev.TxBuffer[0] = reg_addr;
		memcpy(_i2c_dev.TxBuffer+1, reg_data, length);
		_i2c_dev.tx_len = length + 1;
		_i2c_dev.tx_idx = 0;

		// 打开Ack、开中断
		I2C_AcknowledgeConfig(I2C1, ENABLE);
		I2C_ITConfig(I2C1, (I2C_IT_TypeDef)(I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF) , ENABLE);
	
		// Start发送、流程开始
		I2C_GenerateSTART(I2C1, ENABLE);

		// 写完成同步处理,带简单超时判断。如果异步处理可去除这部分代码
		while (_i2c_dev.state != BMP280_I2C_WRITE_REG_END) {
			delay_ms(1);
			if ( count++ >= 300 ) {
				break;
			}
		}
		return 0;
}

4.3 I2C读函数

int8_t i2c_reg_read(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length)
{
		uint8_t count = 0;
		
		// 初始化
		reset_dev(&_i2c_dev);
		
    	// 指定Slave目标地址
		_i2c_dev.addr = i2c_addr;
		_i2c_dev.state = BMP280_I2C_READ_REG1;
		
		// 写缓冲初始化:先写部分的数据是Slave地址
		_i2c_dev.TxBuffer[0] = reg_addr;
		_i2c_dev.tx_len = 1;
		_i2c_dev.tx_idx = 0;
		
		// 读缓冲初始化
		memcpy(_i2c_dev.RxBuffer, 0, sizeof(_i2c_dev.RxBuffer));
		_i2c_dev.rx_len = length;
		_i2c_dev.rx_idx = 0;
		
		// 打开Ack、开中断
		I2C_AcknowledgeConfig(I2C1, ENABLE);
		I2C_ITConfig(I2C1, (I2C_IT_TypeDef)(I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF) , ENABLE);
		
		// Start发送、流程开始
		I2C_GenerateSTART(I2C1, ENABLE);
		
		// Read完成同步处理,带简单超时判断。如果异步处理可去除这部分代码
		while (_i2c_dev.state != BMP280_I2C_READ_REG_END){
			delay_ms(1);
			if ( count++ >= 300 ) {
				break;
			}
		}
		
		// 读出的数据复制到用户缓冲
		memcpy(reg_data, _i2c_dev.RxBuffer, length);
		return 0;
}

4.4 中断处理函数

INTERRUPT_HANDLER(I2C1_IRQHandler, 29)
{
	uint16_t state = 0;
	uint8_t direct = 0;
	
	state = I2C_GetLastEvent(I2C1);
	if( _i2c_dev.state == BMP280_I2C_WRITE_REG ) { // 写处理
		  switch (state) {
					/* EV5 */
				case I2C_EVENT_MASTER_MODE_SELECT :
					/* Send slave Address for write */
					I2C_Send7bitAddress(I2C1, _i2c_dev.addr << 1, I2C_Direction_Transmitter);
					break;
					/* EV6 */
				case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED:
					/* EV8 */
				case I2C_EVENT_MASTER_BYTE_TRANSMITTING:
					if (_i2c_dev.tx_len - _i2c_dev.tx_idx > 0) {
						/* Send the first Data */
						I2C_SendData(I2C1, _i2c_dev.TxBuffer[_i2c_dev.tx_idx]);
						_i2c_dev.tx_idx++;
					} else {
						I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);
					}
					break;		
					/* EV8_2 */
				case I2C_EVENT_MASTER_BYTE_TRANSMITTED:
					/* Send STOP condition */
					I2C_GenerateSTOP(I2C1, ENABLE);
					I2C_ITConfig(I2C1, (I2C_IT_TypeDef)(I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF) , DISABLE);
					_i2c_dev.state = BMP280_I2C_WRITE_REG_END;
					break;
				default:
					break;
			}
	} else if( _i2c_dev.state == BMP280_I2C_READ_REG1 || _i2c_dev.state == BMP280_I2C_READ_REG2 ) { // 读处理
	
		  switch (state)	{
					/* EV5 */
				case I2C_EVENT_MASTER_MODE_SELECT :
					/* Send slave Address for write */
					direct = (_i2c_dev.state == BMP280_I2C_READ_REG1 ? I2C_Direction_Transmitter : I2C_Direction_Receiver);
					I2C_Send7bitAddress(I2C1, (_i2c_dev.addr << 1), direct);
					break;
					/* EV6 */
				case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED:
					/* EV8 */
				case I2C_EVENT_MASTER_BYTE_TRANSMITTING:
					if (_i2c_dev.tx_len - _i2c_dev.tx_idx > 0) {
						I2C_SendData(I2C1, _i2c_dev.TxBuffer[_i2c_dev.tx_idx]);
						_i2c_dev.tx_idx++;
						if( _i2c_dev.tx_len == _i2c_dev.tx_idx ) {
							_i2c_dev.state = BMP280_I2C_READ_REG2;
						}
					}
					break;
					/* EV8_2 */
				case I2C_EVENT_MASTER_BYTE_TRANSMITTED:
					/* Send RESTART condition */
					I2C_GenerateSTART(I2C1, ENABLE);
					break;
				/* EV6 */
				case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED:
					if( _i2c_dev.rx_len == 1 ) {
						I2C_AcknowledgeConfig(I2C1, DISABLE);
						I2C_GenerateSTOP(I2C1, ENABLE);
					}
					break;
				/* EV7 */
				case I2C_EVENT_MASTER_BYTE_RECEIVED:
					_i2c_dev.RxBuffer[_i2c_dev.rx_idx++] = I2C_ReceiveData(I2C1);
					if( _i2c_dev.rx_len - _i2c_dev.rx_idx == 1 ) {
						I2C_AcknowledgeConfig(I2C1, DISABLE);
						I2C_GenerateSTOP(I2C1, ENABLE);
					}
					if(_i2c_dev.rx_len - _i2c_dev.rx_idx <= 0 ) {
						I2C_ITConfig(I2C1, (I2C_IT_TypeDef)(I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF) , DISABLE);
						_i2c_dev.state = BMP280_I2C_READ_REG_END;
					}
					break;
				default:
					break;
			}
	}
}

5、逻辑分析仪对照

Write例:
BMP初始化时需要软重置bmp280,是向0xE0寄存器发送0xB6。如下图:
BMP280 Rest
Read例1:
可以从BMP280获取它的芯片ID(BMP280/BME280),需要读取其0xD0寄存器,如下图:
BMP280读芯片ID
读出结果是0x58表示是BMP280。
能很清楚的看出:先写后读,中间过渡用RepeatStart。

Read例2:
最后,读取BMP280的温度值和气压值,
需要连续读取其从0xF7开始的6字节数据(3字节气压,3字节温度的原始值),如下图:
在这里插入图片描述
能很清楚的看出:先写后读,中间过渡用RepeatStart。

  • 5
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
stm8si2c程序,调试通过 INTERRUPT_HANDLER(I2C_IRQHandler, 19) { /* In order to detect unexpected events during development, it is recommended to set a breakpoint on the following instruction. */ struct SCB_T *system=&system_process; unsigned char i,i2c_sr1,i2c_sr2,i2c_event,i2c__status=0,temp,sr1_analysis_int_resource[8],sr2_analysis_int_resource[8]; char i2c_interrupt_type=0,p;//在一次I2中断中,I2C中断中的中断标志位个数; disableInterrupts();//关总中断 i2c_sr1=I2C->SR1; p=I2C->SR3; i2c_sr2=I2C->SR2; //temp=I2C->SR3; //analysis interrupt resource in i2c->sr1 register sr1_analysis_int_resource[0]=i2c_sr1&I2C_SR1_SB; sr1_analysis_int_resource[1]=i2c_sr1&I2C_SR1_ADDR; sr1_analysis_int_resource[2]=i2c_sr1&I2C_SR1_BTF; sr1_analysis_int_resource[3]=i2c_sr1&I2C_SR1_ADD10; sr1_analysis_int_resource[4]=i2c_sr1&I2C_SR1_STOPF; // sr1_i2c__state[5]=i2c_state&((u8)I2C_SR1_BIT6); sr1_analysis_int_resource[6]=i2c_sr1&I2C_SR1_RXNE; sr1_analysis_int_resource[7]=i2c_sr1&I2C_SR1_TXE; //analysis interrupt resource in i2c->sr2 register sr2_analysis_int_resource[0]=i2c_sr2&I2C_SR2_BERR; sr2_analysis_int_resource[1]=i2c_sr2&I2C_SR2_ARLO; sr2_analysis_int_resource[2]=i2c_sr2&I2C_SR2_AF; sr2_analysis_int_resource[3]=i2c_sr2&I2C_SR2_OVR; sr2_analysis_int_resource[5]=i2c_sr2&I2C_SR2_WUFH; if(sr1_analysis_int_resource[0]==I2C_SR1_SB) {i2c__status=0x01;i2c_interrupt_type++;} if(sr1_analysis_int_resource[1]==I2C_SR1_ADDR) {i2c__status=0x02;i2c_interrupt_type++;} if(sr1_analysis_int_resource[2]==I2C_SR1_BTF) {i2c__status=0x03;i2c_interrupt_type++;} if(sr1_analysis_int_resource[3]==I2C_SR1_ADD10) {i2c__status=0x04;i2c_interrupt_type++;} if(sr1_analysis_int_resource[4]==I2C_SR1_STOPF) {i2c__status=0x05;i2c_interrupt_type++;} if(sr1_analysis_int_resource[6]==I2C_SR1_RXNE) {i2c__status=0x06;i2c_interrupt_type++;} if(sr1_analysis_int_resource[7]==I2C_SR1_TXE) {i2c__status=0x07;i2c_interrupt_type++;} if(sr2_analysis_int_resource[0]==I2C_SR2_BERR) {i2c__status=0x08;i2c_interrupt_type++;} if(sr2_analysis_int_resource[1]==I2C_SR2_ARLO) {i2c__status=0x09;i2c_interrupt_type++;} if(sr2_analysis_int_resource[2]==I2C_SR2_AF) {i2c__status=0x0a;i2c_interrupt_type++;} if(sr2_analysis_int_resource[3]==I2C_SR2_OVR) {i2c__status=0x0b;i2c_interrupt_type++;} if(sr2_analysis_int_resource[5]==I2C_SR2_WUFH) {i2c__status=0x0c;i2c_interrupt_type++;} if(i2c_interrupt_type>=2) /*there are more than one interrupt resource in the time*/ { if(i2c_interrupt_type==2) { if((sr1_analysis_int_resource[1]==I2C_SR1_ADDR)&&(sr1_analysis_int_resource[7]==I2C_SR1_TXE)) { I2C->DR=system->i2c.send_frame.data[system->i2c.send_frame.proc]; system->i2c.send_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x62; } else if((sr1_analysis_int_resource[7]==I2C_SR1_TXE)&&(sr1_analysis_int_resource[2]==I2C_SR1_BTF)) { system->i2c.send_frame.terminate=0; //set I2C transfer terminate bit; system->i2c.send_frame.mod=0; system->i2c.send_frame.write=0; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x64; } else if((sr1_analysis_int_resource[7]==I2C_SR1_TXE)&&(sr2_analysis_int_resource[2]==I2C_SR2_AF)) { I2C->CR2|=I2C_CR2_STOP; I2C->SR2&=(~I2C_SR2_AF);//clear AF bit; system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x64; } else { system->i2c.error=1; I2C_ITConfig(I2C_IT_EVT, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x37; } } else { system->i2c.error=1; I2C_ITConfig(I2C_IT_EVT, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x37; } } else { switch(i2c__status) { case I2C_SR1_SB_proc: //如果是发送模式 if(system->i2c.send_frame.mod==1)//说明本次中断之前是从模式,说明这是在从模式下发的起始位; { //EV5 p=I2C->SR1; I2C->DR=system->i2c.send_frame.add__L; //自动清除I2C_SR1_SB标志 system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x38; } else { if(system->i2c.rev_frame.mod==1) //说明本次中断之间是主模式,这次发的是重复起始位; { //EV6如果是接收模式 p=I2C->SR1; I2C->DR=system->i2c.rev_frame.add__L;//自动清除I2C_SR1_SB标志; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x51; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x36; } } break; case I2C_SR1_ADDR_proc: p=I2C->SR1; temp=I2C->SR3;//软件读取SR1寄存器后,对SR3寄存器的读操作将清除该位 temp&=(u8)I2C_SR3_TRA; I2C->CR2|=(u8)I2C_CR2_ACK;// 使能应答位 I2C->CR2&=(u8)(~I2C_CR2_POS);//设置接受到当字节应答 //如果是发送模式 if(system->i2c.send_frame.mod==1) { if(temp==(u8)I2C_SR3_TRA) {; } else { system->i2c.error=1; } } else { if(system->i2c.rev_frame.mod==1) { if(temp==0)//machine at a master-receive mod { system->i2c.rev_frame.proc=0; } else { system->i2c.error=1; } } else { system->i2c.error=1; } } system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x52; break; case I2C_SR1_RXNE_proc: if(system->i2c.rev_frame.proci2c.rev_frame.num-3)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x57; } else if(system->i2c.rev_frame.proc==(u8)(system->i2c.rev_frame.num-2)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x58; I2C->CR2&=(u8)(~I2C_CR2_ACK);//不返回应答 I2C->CR2|=I2C_CR2_STOP; //发停止位结束这次数据接收; } else if(system->i2c.rev_frame.proc>=(u8)(system->i2c.rev_frame.num-1)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x59; system->i2c.rev_frame.terminate=0; //set I2C transfer terminate bit; system->i2c.rev_frame.mod=0; system->i2c.rev_frame.read=0; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfb; } break; case I2C_SR1_TXE_proc: if(system->i2c.send_frame.proci2c.send_frame.num-1)) { I2C->DR=system->i2c.send_frame.data[system->i2c.send_frame.proc]; system->i2c.send_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x61; } else if(system->i2c.send_frame.proc=(u8)(system->i2c.send_frame.num)) { I2C->CR2|=I2C_CR2_STOP; // 发停止位结束 这次数据接收; I2C_ITConfig(I2C_IT_BUF, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x60; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfc; } break; case I2C_SR2_AF_proc: I2C->CR2|=I2C_CR2_STOP; I2C->SR2&=(~I2C_SR2_AF);//clear AF bit; system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x63; break; default: system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfd; break; } } system->i2c.int_debug_count++; enableInterrupts();//开总中断 }
STM32F103是一款基于ARM Cortex-M3内核的微控制器,有丰富的外设资源。其中包括硬件I2C接口,可以用于连接DS3231时钟芯片。 DS3231是一款高精度的实时时钟芯片,可以提供秒、分、时、日、月、年等多种时间信息,并支持温度补偿功能。 在实现硬件I2C读写DS3231的程序中,需要先定义好相关的I2C参数,包括芯片地址、传输速率等。然后通过STM32F103的I2C外设库函数,可以方便地向DS3231写入或读取相关的数据。 需要注意的是,DS3231芯片提供了多种时间格式,如BCD码、十进制等。在程序中需要根据具体的应用场景选择合适的时间格式,并进行相应的转换。 代码示例: 1. 定义I2C参数 #define DS3231_ADDR 0xD0 #define I2C_SPEED 200000 2. 初始化I2C接口 I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED; I2C_Cmd(I2C1, ENABLE); 3. 写入时间信息 uint8_t TimeBuf[7]; for(int i=0; i<7; i++){ TimeBuf[i] = ... //根据具体格式填充时间数据 } I2C_Start(); I2C_SendData(DS3231_ADDR<<1); I2C_WaitAck(); I2C_SendData(0x00); I2C_WaitAck(); for(int i=0; i<7; i++){ I2C_SendData(TimeBuf[i]); I2C_WaitAck(); } I2C_Stop(); 4. 读取时间信息 uint8_t TimeBuf[7]; I2C_Start(); I2C_SendData(DS3231_ADDR<<1); I2C_WaitAck(); I2C_SendData(0x00); I2C_WaitAck(); I2C_Start(); I2C_SendData((DS3231_ADDR<<1)|0x01); I2C_WaitAck(); for(int i=0; i<6; i++){ TimeBuf[i] = I2C_ReceiveData(); I2C_Ack(); } TimeBuf[6] = I2C_ReceiveData(); I2C_Stop(); 以上代码示例仅为示范,实际使用时需要根据具体情况进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值