STM32 硬件I2C驱动 MCP4728

前言

  • MCP4728是12位四通道电压数模转换器(DAC),具有非易失性存储器(EEPROM),其片上精密输出放大器使其能够输出达到轨至轨模拟输出摆幅。用户可使用I2C串行接口命令将DAC输入代码、器件配置位和I2C地址位烧写到EEPROM中。EEPROM功能使得DAC器件在断电器件仍然能够保持DAC输入代码,且在DAC上电后根据保存的设置立即产生输出
  • 大部分网友表示STM32自带的硬件IIC存在bug,读写时很容易卡死,这里我要说明一下,可能会出现上述情况,但是硬件I2C完全可用,出现卡死的情况说明你的I2C驱动程序配置错误或者未正确读取从机地址等等。本文是用STM32硬件I2C主机编写的MCP4728的驱动,完全可以正常使用,不会出现卡死现象。
  • 下面是关于网上一些对STM32 I2C的功能的描述和例程
    IIC的基本原理
    STM32F103-硬件IIC通信
    STM32系统学习——I2C (读写EEPROM)

MCP4728内部寄存器配置位描述

 MCP4728内部寄存器配置非常灵活,用户可以根据实际需要配置相关寄存器即可。
在这里插入图片描述

  • PDY/BSY仅作为状态指示,可以不进行配置,硬件电路上可以浮空
  • A2、A1、A0为器件地址位,出厂设置为000,如果需要更多的MCP4278用户可以自定义器件地址
  • PD1、PD2一般设置为00,正常模式,所有通道都可输出
  • UDAC’仅影响选定的通道;在硬件电路上LADC’引脚设置拉低,更新所有DAC通道
                                 在这里插入图片描述
  • 用户通过配置DAC1、DAC0选择输出通道,配置Vref、Gx可以计算输出通道的输出电压大小
                                 在这里插入图片描述
  • 文中的Vref = 1;Gx = 1;UDAC = 0

写DAC寄存器和EEPROM

 使用三个写命令类型位(C2、C1、C0)和两个写功能位(W1和W0)来定义写命名。
在这里插入图片描述

自定义MCP4728地址

器件寻址:
地址字节是主器件启动后接收到的第一个字节,地址字节的第一部分是4位器件代码(对于MCP4728器件,设置为1100),器件代码后面跟三个地址位(A2、A1和A0),这三个地址可由用户进行编程,出厂默认为000
                    在这里插入图片描述
I2C地址编程:
(a)使用“广播呼叫读地址”命令读取地址,简单的说就是当你不知道器件地址位多少时,可以读取地址位,一般出厂默认是0x11000000 = 0xC0,除非你买非原厂或者是别人用过的芯片地址位已经被更改
(b)使用“写I2C地址位”命令自定义地址位,编程A2、A1和A0,下图是向DAC寄存器和EEPROM写I2C地址位

     在这里插入图片描述
注:
1、时钟脉冲和LDAC的跳变细节
2、在第2个字节和第3个字节时LDAC发生的事件
(a)保持LDAC引脚为“高电平”直到第2个字节的第8个时钟的正脉冲结束
(b)LDAC引脚在第2个字节的第8个时钟的负脉冲(恰好在第9个时钟的上升沿之前)产生从“高电平”到“低电平”的跳变,并保持到第3个字节的第9个时钟上升沿
(c)如果a、b不满足,则MCP4728器件不应答第3个字节
3、LDAC引脚在“停止”位后保持低电平
4、更改地址只能用软件I2C实现(为了保证时序上的匹配),硬件I2C不能实现


小技巧“硬件上LDAC可以直接拉低,更改地址时可以跳线到高电平”

驱动代码:

/*****************************************************************************
 * @fn      I2C1_Init
 *
 * @brief   I2C1 -- IO port initialization; I2C1 basic parameter configuration
 *
 * @param   None
 *          
 * @return  None
 *****************************************************************************/
void I2C1_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	GY_I2C_GPIO_CLK, ENABLE );		 	 		//使能GPIOB时钟
	   
	GPIO_InitStructure.GPIO_Pin = GY_I2C_SCL_PIN|GY_I2C_SDA_PIN;        //PB6 ->SCL;  PB7->SDA
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   		 		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*****************************************************************************
 * @fn      I2C1_Start
 *
 * @brief   Start
 *
 * @param   None
 *          
 * @return  None
 *****************************************************************************/
void I2C1_Start(void)
{
	SDA_OUT();    	      //sda线设置为输出
	I2C1_SDA = 1;	  	  
	I2C1_SCL = 1;
	delay_us(4);
 	I2C1_SDA = 0;    	 //START:when CLK is high,DATA change form high to low 
	delay_us(4);	
	I2C1_SCL = 0;     	 //钳住I2C总线,准备发送或接收数据 
}

/*****************************************************************************
 * @fn      I2C1_Stop
 *
 * @brief   Stop
 *
 * @param   None
 *          
 * @return  None
 *****************************************************************************/
void I2C1_Stop(void)
{
	SDA_OUT();		    //sda线输出
	I2C1_SCL = 0;
	I2C1_SDA = 0;		//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	I2C1_SCL = 1; 
	I2C1_SDA = 1;        //发送I2C总线结束信号
	delay_us(4);							   	
}

/*****************************************************************************
 * @fn      I2C1_Wait_Ack
 *
 * @brief   Waiting for the response signal to arrive
 *
 * @param   None
 *          
* @return  false:Receive response failed // true:Receive response success
 *****************************************************************************/
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
uint8_t I2C1_Wait_Ack(void)
{
	uint8_t ucErrTime = 0;
	
	SDA_IN();      				//SDA设置为输入  
	I2C1_SDA = 1;delay_us(1);	   
	I2C1_SCL = 1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			I2C1_Stop();
			return 1;
		}
	}
	I2C1_SCL = 0;				//时钟输出0 	   
	return 0;  
}

/*****************************************************************************
 * @fn      I2C1_Ack
 *
 * @brief   I2C1 Ack
 *
 * @param   None
 *          
 * @return  None
 *****************************************************************************/
void I2C1_Ack(void)
{
	I2C1_SCL = 0;
	SDA_OUT();
	I2C1_SDA = 0;
	delay_us(2);
	I2C1_SCL = 1;
	delay_us(2);
	I2C1_SCL=0;
}

/*****************************************************************************
 * @fn      I2C1_NAck
 *
 * @brief   I2C1 No Ack
 *
 * @param   None
 *          
 * @return  None
 *****************************************************************************/
//不产生ACK应答		    
void I2C1_NAck(void)
{
	I2C1_SCL = 0;
	SDA_OUT();
	I2C1_SDA = 1;
	delay_us(2);
	I2C1_SCL = 1;
	delay_us(2);
	I2C1_SCL = 0;
}
/*****************************************************************************
 * @fn      I2C1_Send_One_Byte
 *
 * @brief   I2C1 send one byte,Return to the slave to see if there is a response
 *			ture:No Ack // false: Ack
 *
 * @param   txd:Send Data
 *          
 * @return  None
 *****************************************************************************/		  
void I2C1_Send_One_Byte(uint8_t txd)
{                        
    uint8_t t;   
	
	SDA_OUT(); 	  
	
    I2C1_SCL=0;						//拉低时钟开始数据传输
    for(t=0;t<8;t++)    			//开始准备信号线
    {              
		I2C1_SDA=(txd&0x80)>>7;
		txd<<=1; 	  
		delay_us(2);  				 //这三个延时都是必须的
		I2C1_SCL=1;
		delay_us(2); 
		I2C1_SCL=0;	
		delay_us(2);
    }	 
}
/*****************************************************************************
 * @fn      I2C1_Read_One_Byte
 *
 * @brief   Read 1 byte, ack=1, send ACK, ack=0, send nACK
 *
 * @param   None
 *          
 * @return  None
 *****************************************************************************/ 
uint8_t I2C1_Read_One_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	
	SDA_IN();						//SDA设置为输入
	
    for(i=0;i<8;i++ )
	{
        I2C1_SCL=0; 
        delay_us(2);
		I2C1_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        I2C1_NAck();					//发送nACK
    else
        I2C1_Ack(); 					//发送ACK   
    return receive;
}

/********************************************************************************************
 * @fn      change_address
 *
 * @brief   I2C_Master MCP4278 select and output channel select, control LED output current
 *			
 * @param   Cmd_NewAdd --- U12_MCP4728:0xC2//U13_MCP4728:0xC4//U14_MCP4728:0xC6
 *
 * @return  none
 ******************************************************************************************/
void change_address(uint8_t Cmd_NewAdd)
{
	
	I2C1_Start(); 
	LDAC_ON;									//LDAC引脚为高电平
	I2C1_Send_One_Byte(MCP4728DFADD);	    	//器件地址	      0xC0
	I2C1_Wait_Ack();	   
	I2C1_Send_One_Byte(Cmd_DefaultAdd);   		//发送命令+当前地址 0x61
	LDAC_OFF;									//LDAC引脚为低电平
	I2C1_Wait_Ack(); 	 										  		   
	I2C1_Send_One_Byte(Cmd_NewAdd);     		//发送新地址						   
	I2C1_Wait_Ack(); 
	I2C1_Send_One_Byte(Cmd_NewAdd | 0x01);     //发送新地址	
	I2C1_Wait_Ack(); 
	I2C1_Stop();							   //产生一个停止条件 
	delay_ms(10);
}

单次写命令:选择单个输出通道写入DAC输入寄存器

I2C单次写时序与字节数如下:
在这里插入图片描述
驱动代码:

/********************************************************************************************
 * @fn      mcp4728_write_signle
 *
 * @brief   This command writes to the DAC input registers and EEPROM sequentially from a start
 *			channel to the channel D
 *			
 * @param   I2Cx --- I2C1 // I2C2
 *          DevcieAdd_MCP4728 --- Enter a redefined address
 *			Cmd_Channel --- 0x58:VoutA //0x5A:VoutB // 0x5C:VoutC // 0x5E:VoutD
 *			Vout --- mV
 *
 * @return  I2C_OK
 ******************************************************************************************/
I2C_StatusTypeDef mcp4728_write_signle(I2C_TypeDef* I2Cx, uint8_t DevcieAdd_MCP4728, uint8_t Cmd_Channel, double Vout)
{
	uint16_t Dn;
	uint8_t Hbyte;
	
	Dn = Vout * 1000;
	
	Hbyte = (((0x0F00 & Dn) >> 8) |(PREFDEFINE));  //PREFDEFINE = 0x90 
	
	/* Test on BUSY Flag BUSY = 1:data byte transmitted*/
	_time_out = LONG_TIMEOUT;
	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)){
#if Timeout_SoftReset        
		if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
		}
#endif	
    }
	
	/* start */
	I2C_GenerateSTART(I2Cx,ENABLE);	

	_time_out = LONG_TIMEOUT;
	
	/* EV5事件 SB=1,MSL=1,BUSY=1 起始条件已经发送了,然后是主模式,总线在通讯 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif	
    }
   
	/* Send Device Address 最后一个参数表示地址bit0为0 是写操作 cleared by writing DR regisetr */
	I2C_Send7bitAddress(I2Cx, DevcieAdd_MCP4728, I2C_Direction_Transmitter); 

	_time_out = LONG_TIMEOUT;
	
	/*EV6 EV8_1事件  ADDR =1(地址已经发送) TXE = 1(数据寄存器非空) */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif	
	}
   
	/* Send Cmd_WriteModel  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Cmd_Channel);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8_2事件  TXE = 1 BTF = 1*/
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	 /*EV8_2事件中的程序写停止*/
	I2C_GenerateSTOP(I2Cx,ENABLE);  
	
	return I2C_OK;
}

注:调试中发现,假如通道A和通道B需要同时输出,此时程序中简单的写了两条单次写命令

mcp4728_write_signle(I2C1, U7_MCP4728, S_VoutA, BackIRLedVout);
mcp4728_write_signle(I2C1, U7_MCP4728, S_VoutB, BackIRLedVout);
当程序运行时,通道A和通道B确实可以输出,但是如果想要同时更改通道A和通道B的输出电压大小,发现只有通道A会变,通道B不会变。这个时候单次写命令就不起作用,此时就需要其他写命令

快速写命令:按通道A至通道D顺序写入DAC输入寄存器

I2C快速写时序与字节数如下:
                 在这里插入图片描述
驱动代码:

/********************************************************************************************
 * @fn      mcp4728_write_fast
 *
 * @brief   The data are sent sequentially from channel A to the channel D. 
 *			
 * @param   I2Cx --- I2C1 // I2C2
 *          DevcieAdd_MCP4728 --- Enter a redefined address
 *			Vout --- mV
 *
 * @return  I2C_OK
 ******************************************************************************************/
I2C_StatusTypeDef mcp4728_write_fast(I2C_TypeDef* I2Cx, uint8_t DevcieAdd_MCP4728, double Vout)
{
	uint16_t Dn;
	uint8_t Hbyte;
	
	Dn = Vout * 1000;
	
	Hbyte = ((0x0F00 & Dn) >> 8);
	
	/* Test on BUSY Flag BUSY = 1:data byte transmitted*/
	_time_out = LONG_TIMEOUT;
	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)){
#if Timeout_SoftReset        
		if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
		}
#endif	
    }
	
	/* start */
	I2C_GenerateSTART(I2Cx,ENABLE);	

	_time_out = LONG_TIMEOUT;
	
	/* EV5事件 SB=1,MSL=1,BUSY=1 起始条件已经发送了,然后是主模式,总线在通讯 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif	
    }
   
	/* Send Device Address 最后一个参数表示地址bit0为0 是写操作 cleared by writing DR regisetr */
	I2C_Send7bitAddress(I2Cx, DevcieAdd_MCP4728, I2C_Direction_Transmitter); 

	_time_out = LONG_TIMEOUT;
	
	/*EV6 EV8_1事件  ADDR =1(地址已经发送) TXE = 1(数据寄存器非空) */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif	
	}  
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8_2事件  TXE = 1 BTF = 1*/
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	 /*EV8_2事件中的程序写停止*/
	I2C_GenerateSTOP(I2Cx,ENABLE);  
	
	return I2C_OK;
}

注1:不要选择通道,仅需要设置PD1 = 0、PD0 = 0即可                                      注2:通道A至通道D都被激活,所有通道输出电压大小相同并且能够同时被更改为相同大小

多次写命令:选择任意输出通道写入DAC输入寄存器

I2C多次写时序与字节数如下:
                 在这里插入图片描述
驱动代码:

/********************************************************************************************
 * @fn      mcp4728_write_multi
 *
 * @brief   This command writes to the multiple DAC input registers, one register at a time.The
 *			writing channel register is defined by the DAC selection bits (DAC1, DAC0). 
 *			
 * @param   I2Cx --- I2C1 // I2C2
 *          DevcieAdd_MCP4728 --- Enter a redefined address
 *			Cmd_Channel --- 0x40:VoutA //0x02:VoutB // 0x04:VoutC // 0x06:VoutD
 *			Vout --- mV
 *
 * @return  I2C_OK
 ******************************************************************************************/
I2C_StatusTypeDef mcp4728_write_multi(I2C_TypeDef* I2Cx, uint8_t DevcieAdd_MCP4728, uint8_t Cmd_Channel1, double Vout1, uint8_t Cmd_Channel2, double Vout2)
{
	uint16_t Dn1,Dn2;
	uint8_t Hbyte1,Hbyte2;
	
	Dn1 = Vout1 * 1000;
	
	Hbyte1 = (((0x0F00 & Dn1) >> 8) |(PREFDEFINE));
	
	Dn2 = Vout1 * 1000;
	
	Hbyte2 = (((0x0F00 & Dn2) >> 8) |(PREFDEFINE));
	
	/* Test on BUSY Flag BUSY = 1:data byte transmitted*/
	_time_out = LONG_TIMEOUT;
	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)){
#if Timeout_SoftReset        
		if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
		}
#endif	
    }
	
	/* start */
	I2C_GenerateSTART(I2Cx,ENABLE);	

	_time_out = LONG_TIMEOUT;
	
	/* EV5事件 SB=1,MSL=1,BUSY=1 起始条件已经发送了,然后是主模式,总线在通讯 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif	
    }
   
	/* Send Device Address 最后一个参数表示地址bit0为0 是写操作 cleared by writing DR regisetr */
	I2C_Send7bitAddress(I2Cx, DevcieAdd_MCP4728, I2C_Direction_Transmitter); 

	_time_out = LONG_TIMEOUT;
	
	/*EV6 EV8_1事件  ADDR =1(地址已经发送) TXE = 1(数据寄存器非空) */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif	
	}
   
	/* Send Cmd_WriteModel  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Cmd_Channel1);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte1);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn1);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	/* Send Cmd_WriteModel  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Cmd_Channel2);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte2);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn2);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8_2事件  TXE = 1 BTF = 1*/
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	 /*EV8_2事件中的程序写停止*/
	I2C_GenerateSTOP(I2Cx,ENABLE);  
	
	return I2C_OK;
}
注:选择任意两路输出通道,可以设置两路输出通道输出电压带下

连续写命令:选择起始通道至通道D连续写入DAC输入寄存器

I2C连续写时序与字节数如下:
               在这里插入图片描述
驱动代码:

/********************************************************************************************
 * @fn      mcp4728_write_sequential
 *
 * @brief   I2C_Master MCP4278 select and output channel select, control LED output current
 *			
 * @param   I2Cx --- I2C1 // I2C2
 *          DevcieAdd_MCP4728 --- Enter a redefined address
 *			Cmd_Channel --- 0x50:VoutA //0x52:VoutB // 0x54:VoutC // 0x56:VoutD
 *			Vout --- mV
 *
 * @return  I2C_OK
 ******************************************************************************************/
I2C_StatusTypeDef mcp4728_write_sequential(I2C_TypeDef* I2Cx, uint8_t DevcieAdd_MCP4728, uint8_t Cmd_Channel, double Vout)
{
	uint16_t Dn;
	uint8_t Hbyte;
	
	Dn = Vout * 1000;
	
	Hbyte = (((0x0F00 & Dn) >> 8) |(PREFDEFINE));

	/* Test on BUSY Flag BUSY = 1:data byte transmitted*/
	_time_out = LONG_TIMEOUT;
	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)){
#if Timeout_SoftReset        
		if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
		}
#endif	
    }
	
	/* start */
	I2C_GenerateSTART(I2Cx,ENABLE);	

	_time_out = LONG_TIMEOUT;
	
	/* EV5事件 SB=1,MSL=1,BUSY=1 起始条件已经发送了,然后是主模式,总线在通讯 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif	
    }
   
	/* Send Device Address 最后一个参数表示地址bit0为0 是写操作 cleared by writing DR regisetr */
	I2C_Send7bitAddress(I2Cx, DevcieAdd_MCP4728, I2C_Direction_Transmitter); 

	_time_out = LONG_TIMEOUT;
	
	/*EV6 EV8_1事件  ADDR =1(地址已经发送) TXE = 1(数据寄存器非空) */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif	
	}
   
	/* Send Cmd_WriteModel  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Cmd_Channel);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	/* Send Hbyte  TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Hbyte);

	_time_out = LONG_TIMEOUT;
	
	/* EV8事件  TXE = 1 */
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0) {
			return i2c_timeout_callback(I2Cx);
        }
#endif			
	}

	/* Send Lbyte of Dn TXE = 0 cleared by writing DR regisetr*/
	I2C_SendData(I2Cx,Dn);
  
	_time_out = LONG_TIMEOUT;
	
	/* EV8_2事件  TXE = 1 BTF = 1*/
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
#if Timeout_SoftReset	   
	   if((_time_out--) == 0){
			return i2c_timeout_callback(I2Cx);
        }
#endif		
	}
	
	 /*EV8_2事件中的程序写停止*/
	I2C_GenerateSTOP(I2Cx,ENABLE);  
	
	return I2C_OK;
}

其他写命令

例如关断通道写命令、写增益命令等等,再次不一一叙述,同上述I2C写命令一样,只要根据Datasheet中所给的时序编写即可

资料下载地址
Datasheet、User’s Guide

发布了5 篇原创文章 · 获赞 7 · 访问量 379

猜你喜欢

转载自blog.csdn.net/weixin_40727233/article/details/104303962