文章目录
前言
- 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