[技术讨论]

原创分享:STM8S003硬件I2C通信驱动12位DAC芯片MCP4725控制程序

[复制链接]
705|5
手机看帖
扫描二维码
随时随地手机跟帖
henangongda123|  楼主 | 2021-4-24 16:18 | 显示全部楼层 |阅读模式
#申请原创# MCP4725模块是某宝上买的,调试了一下午,有个误区大家注意下,当器件地址是0x60、0x61时,注意要左移1位,实际器件地址是0xC0(写指令)、0xC1(读指令);其他地方还是以前踩过的STM8S硬件I2C坑,因为之前踩过,所以这次很容易就过去了
1.png
2.png
3.png
4.png

使用特权

评论回复
评论
henangongda123 2021-4-26 10:24 回复TA
@21小跑堂 :还有这要求啊 好吧 
21小跑堂 2021-4-26 10:16 回复TA
感谢参与原创奖励活动,此篇文章因字数未达到800字(此项为硬性指标,代码不计入字数统计)所以暂未通过审核。选题很不错,可以试试丰富完善下哦! 

相关帖子

henangongda123|  楼主 | 2021-4-24 16:22 | 显示全部楼层
程序来了~
/********** STM8S硬件I2C相关操作 **********/
//初始化函数
/***********************************************************
函数名: I2C_Init()
功  能: STM8S103 MCU硬件I2C初始化
        (注: 主模式,两线连接,7位地址模式,10Kbps通信速率)
参  数: 无
返回值: 无
***********************************************************/
void I2C_Init(void)
{
  I2C_CR1 &= 0xFE;           //PE=0,禁用硬件I2C模块(所有位置0)
        CLK_PCKENR1 |= 0x01;        //使能I2C时钟
        I2C_CR2 |= 0x80;           //I2C_CR2控制寄存器SWRST=1,软件复位硬件I2C模块
        I2C_CR2 &= 0x7F;           //I2C_CR2控制寄存器SWRST=0,软件复位硬件I2C模块结束
  I2C_FREQR = 0x02;           //配置I2C外设时钟2MHz
        I2C_CCRH = 0x00;            //配置I2C为标准模式及速率
        I2C_CCRL = 0x64;            //I2C通信速率半周期=((1/2)*100us=50us,速率=1/100us=10KHz
        I2C_TRISER = 0x03;          //配置I2C上升时间寄存器,1000ns(SCL最大值)/500ns(2MHz周期)+1
        I2C_CR1 |= 0x01;            //启动I2C硬件模块
}
/***********************************************************
函数名: MCP4725_Write()/SCL-PB4,SDA-PB5/器件地址:0xC0写指令,0xC1读指令
功  能: STM8S103 MCU通过硬件I2C通信向从设备MCP4725写入器件地址、数据
参  数: data_buff --- 欲写入的多字节数据指针
                          data_len  --- 欲写入的多字节数据长度
返回值: 无
***********************************************************/
void MCP4725_Write(uchar *data_buff,uchar data_len)
{         
        uchar temp;                 //定义一下,读取MCU状态寄存器用
        uchar i;                    //定义一下,供循环使用
        uint time;                  //定义一下,供等待延时用
        //以下程序为I2C通信防锁死操作,非常重要!
        if((PB_IDR&0x20) == 0x00)  //检测总线是否真的忙碌中(忙碌则SCL=1,SDA=0)
        {
                I2C_CR1 &= 0xFE;         //PE=0,禁用硬件I2C模块,准备直接操作MCU端口
                PB_DDR |= 0x10;           //设置SCL端口为输出
                PB_CR1 |= 0x10;           //上拉输出
                for(i=0;i<9;i++)         //SCL端口发出9个时钟脉冲,让从设备MCP4725释放总线(恢复SDA=1)
                {
                        PB_ODR &= (~0x10);      //SCL=0;
                        delay_us(50);           //延时(I2C通信频率10KHz)
                  PB_ODR |= 0x10;         //SCL=1
                  delay_us(50);           //延时(I2C通信频率10KHz)
                }
                time = 500;               //变量赋值,准备延迟等待500次
                while(!(PB_IDR&0x20))     //等待数据线SDA=1
                {
                  if(!--time)             //变量自减
                  return;                 //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
          }
                PB_DDR &= (~0x10);        //SCL端口恢复输入方式
                PB_CR1 &= (~0x10);        //浮空输入
                PB_ODR &= (~0x10);        //清零输出寄存器
                I2C_CR1 |= 0x01;         //重新启动硬件I2C模块,接管SCL、SDA端口
                return;                   //退出函数,放弃本次数据写操作
        }
        //以上程序为I2C通信防锁死操作,非常重要!
        //以下程序为STM8S硬件I2C通信防BUSY锁死操作,非常重要!
        if((I2C_SR3&0x02) == 0x02) //检测总线是否正确释放空闲中(总线正确释放则I2C_SR3状态寄存器BUSY=0,否则BUSY=1)
        {
                I2C_CR2 |= 0x80;         //I2C_CR2控制寄存器SWRST=1,软件复位硬件I2C模块
          I2C_CR2 &= 0x7F;         //I2C_CR2控制寄存器SWRST=0,软件复位硬件I2C模块结束
                I2C_FREQR = 0x02;         //配置I2C外设时钟2MHz
                I2C_CCRH = 0x00;          //配置I2C为标准模式及速率
                I2C_CCRL = 0x64;          //I2C通信速率半周期=((1/2)*100us=50us,速率=1/100us=10KHz
          I2C_TRISER = 0x03;        //配置I2C上升时间寄存器,1000ns(SCL最大值)/500ns(2MHz周期)+1
                I2C_CR1 |= 0x01;          //启动I2C硬件模块
                return;                   //退出函数,放弃本次数据写操作
        }
        //以上程序为STM8S硬件I2C通信防BUSY锁死操作,非常重要!
        I2C_CR2 &= 0xFB;           //关闭ACK应答(清零I2C_CR2控制寄存器ACK位,收到一个字节数据或地址后不返回应答)
        time = 500;                 //变量赋值,准备延迟等待500次
        while(I2C_SR3&0x02)         //检测总线是否忙碌中(I2C_SR3状态寄存器BUSY位=1表示总线上有通信/忙碌)
        {
                if(!--time)               //变量自减
                return;                   //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
        }
        I2C_CR2 |= 0x01;            //发起始条件(置位I2C_CR2控制寄存器START位)
        time = 500;                 //变量赋值,准备延迟等待500次
        while(!(I2C_SR1&0x01))     //I2C_SR1状态寄存器SB位=1表示发送成功
        {
                if(!--time)               //变量自减
                return;                   //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
        }
        delay_us(5);                //短暂延时
        temp = I2C_SR1;             //读取状态寄存器I2C_SR1值
        I2C_DR = 0xC0;              //发送MCP4725从设备地址,同时清除I2C_SR1中BFT位(SLAVE ADDRESS,末位R/W=0表示写操作)
        time = 500;                 //变量赋值,准备延迟等待500次
        while(!(I2C_SR1&0x02))     //I2C_SR1状态寄存器ADDR位=1时表示地址发送结束(主模式)
        {
                if(!--time)               //变量自减
                return;                   //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
        }
        delay_us(5);                //短暂延时
        temp = I2C_SR1;             //读取状态寄存器I2C_SR1数值
        temp = I2C_SR3;            //清除状态寄存器I2C_SR1中ADDR标志位(地址已发送结束)
        while(data_len)             //循环写入多字节数据
        {
                I2C_DR = *data_buff;      //发送一字节数据
          while(!(I2C_SR1&0x84));   //等待数据发送完毕(I2C_SR1状态寄存器TxE、BTF位为1时,表示发送时数据寄存器为空且数据字节发送完毕)
                data_buff++;              //准备写入下一字节数据
                data_len--;
        }
        I2C_CR2 |= 0x02;            //发停止条件(置位I2C_CR2控制寄存器STOP位///必须先清除I2C_SR1状态寄存器BTF位)
}
/***********************************************************
函数名: Voltage_Write()
功  能: STM8S103通过硬件I2C通信向 12位DAC芯片MCP4725写入输出电压数据
参  数: 无
返回值: 无
***********************************************************/
void Voltage_Write(void)
{
        uint i;                     //定义一下,临时使用数据
        i = Light_Brightness*40.96; //计算当前亮度对应12位DAC值
        MCP4725_Data[0] = (i&0x0F00)>>8;//快速模式,不关断,获取电压DAC值高4位
        MCP4725_Data[1] = i&0x00FF; //获取电压DAC值低8位
        MCP4725_Write(MCP4725_Data,2);//写入12位电压DAC数值
}

使用特权

评论回复
Prry| | 2021-4-25 11:39 | 显示全部楼层
赞!关于i2c地址,很易误解,主要是地址位问题。实质上,i2c器件的有效地址是7bit,不包括地址位!不同人写的i2c驱动接口,使用的地址不同,易入坑。
如Linux、RT-Thread的i2c驱动地址使用的是7bit,在驱动内部做处理;一些个人编写的模拟i2c使用8bit地址(包括读写位)。另外也要注意芯片手册描述的i2c地址,
通常描述的都是7bit,也见过有个别描述的是8bit。区分7bit还是8bit地址,如果值大于0x7F,肯定是8bit地址了;最可靠的判断就是看时序图!
建议,i2c驱动库入口函数的i2c地址保留为7bit,读写位由驱动内部处理,用户只传入标准7bit地址,避免入坑。

使用特权

评论回复
henangongda123|  楼主 | 2021-4-25 11:54 | 显示全部楼层
Prry 发表于 2021-4-25 11:39
赞!关于i2c地址,很易误解,主要是地址位问题。实质上,i2c器件的有效地址是7bit,不包括地址位!不同人 ...

高!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

42

主题

2116

帖子

19

粉丝