嵌入式开发学习笔记:使用STM32G431和HAL库控制数字电位器MCP4017

系列文章目录

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记01:赛事介绍与硬件平台

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记02:开发环境安装

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记03:G4时钟结构

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记04:从零开始创建工程模板并开始点灯

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记05:Systick滴答定时器

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记06:按键输入

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记07:ADC模数转换

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记08:LCD液晶屏

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记09:EEPROM

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记10:USART串口通讯

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记11:数字电位器MCP4017


目录

系列文章目录

前言

一、电路原理

二、程序设计

三、ADC采样分压电压


前言

数字电位器MCP4017本质是一个IC器件,也就是说它是一个集成芯片,而板子上的蓝色旋转电位器R37、R38本质是一个电阻。数字电位器本质是一个芯片,但是内部有一个电阻网络,通过很多模拟开关来给电阻网络切换不同的阻值,我们是通过之前介绍过的I2C总线来给芯片发送数据,告诉芯片我要打开哪些模拟开关,从而控制它的阻值。

一、电路原理

首先我们看一下MCP4017,首先1号管脚接3.3V,2号管脚接GND,3、4管脚是I2C的通讯线,最重要的是5、6,数字电位器W、B两个管脚,5脚相当于是一个可变电阻的一端,另一端是悬空的,6脚相当于是可变电阻的滑片。

我们假设W滑到最左边,则5、6之间的电阻R_{WB}就等于AB之间整个电阻的总值R_{AB},如果滑到AB的中点,那么R_{WB}=\frac{1}{2}R_{AB},如果滑到B端,那么R_{WB}就约等于0。需要注意的是,芯片内部并不是这样的结构,而是一堆模拟开关实现了这样的功能。因为模拟开关本身是有阻值的,所以也会存在一些误差。

这个电位器的型号是104ELT(原理图可见),也就是说最多有10*10^4欧姆的电阻可调,即100kΩ。

那么我们看电路图,W端接了一个10k的电阻接到3.3V,B端接地,很容易看出这是一个分压电路,那么我们很容易可以算出W端的电位:V=3.3\cdot \frac{R_{WB}}{R_{WB}+10}。而这个分压的电压V是接到了单片机的PB14这个口,PB14在STM32上也是一个ADC的输入口,所以我们可以通过ADC来采集这个电压的分压情况,从而可以知道WB之间的阻值变化。当然我们也可以设置好一个阻值,然后通过ADC来确认它的电压。

通过对这个电路的分析我们可知,我们实际上就是通过一个10k的电阻与一个数字电位器分压来得到PB14口的模拟输入。

那么我们接下来简单了解一下MCP4017这个芯片。可以看到它是一个7bit的支持IIC总线的数字电位器,并且拥有可变内存。SC70是它的封装。401系列还有4018、4019,其中4018一般用于分压器,4017、4019一般用于可变电阻,具体的我们就不去了解了。

 

  • 7bit是指它的电阻网络有7个位可以配置,也就是可以配置一共127个不同的阻值。因为总阻值是100kΩ,那么我们就可以把100k分成127个档。
  • 抽头的电阻是100Ω,也就是说如果把WB短接,那也有100Ω的阻值(几乎可忽略)。
  • 低温漂:可变电阻的温漂是50ppm(百万分之50)。
  • 用IIC协议来读写。
  • 掉电时WB的电压不能超过1.5V。
  • 不编任何程序的话,它默认上电就是中间电压(50kΩ)。
  • 低功耗(2.5微安)
  • 电压工作范围:2.7V-5.5V。
  • 还有一点就是芯片的存储形式是RAM,也就是说每次掉电都会清空,这就是为什么要规定默认上电时要中间电压。

    除此之外比较重要的就是MCP4017的器件地址:0101111,还有一个R/W位,如果是写入的话,R/W就是0,加上器件地址就是01011110,即0x5e,如果是读就是0x5f。

    我们再看一下内部的电阻网络是怎么构成的。可以看到电阻网络还是挺简单的,我们甚至可以自己用电阻加模拟开关来构成一个类似的电路,当然,芯片帮我们集成好了用起来更方便。

    可以看到每个Rs的电阻上都加了一个开关,每个开关上都有一个数,也就是说我们每往上增加一个数,就会增加一个Rs的电阻。Rs一共有127个,加起来等于100kΩ,这样我们可以算出Rs的值应该是0.7874kΩ。比方说:如果控制字节是0x7F,那么电阻就是100kΩ,如果控制字节是0x3F,就是中间位,即50kΩ,如果控制字节是0x00,那么电阻就是0(实际上是100Ω)。

    二、程序设计

    我们可以利用I2C总线控制数字电位器芯片的电阻值,与R17的10kΩ形成分压,这样PB14的电位就会发生相应的变化,再用ADC来进行读取。

    这里我们先看一下怎么编程STM32来改变MCP4017的阻值。

    程序流程:

  • 复制i2c的底层驱动文件到“编程工程”并改名
  • 在main.c调用i2c的IO初始化代码并包含头文件
  • 在i2c.c文件里编程:读写MCP4017函数,并在i2c.h中声明
  • 在main.c调用MCP4017_Write和MCP4017_Read完成程序
  • 其中重点是MCP4017读写函数的编写,其他的部分我们在EEPROM里面已经做过了就不赘述了。我们这里主要看一下怎么编写MCP4017读写函数。下图是厂商芯片资料给的如何写一个字节数据的示例。

    主器件就是我们的STM32,首先要发一个Start起始命令,然后发送从器件的地址+一个R/W位,因为是写数据,所以R/W位是0,所以是0x5E。然后等待应答。完成后,我们要送一个数据给MCP4017,送的数据就是我们要控制它的电阻的大小,它是一个7bit的控制字,就是0-0x7F的控制范围。主设备把这个字节发送过去,第一位用不到所以丢弃。然后等待应答。最后主器件再发一个终止命令STOP。

    根据这个图我们很容易就可以写出MCP4017_Write()函数:

    void MCP4018_Write(u8 val)
    {
        I2CStart();
        I2CSendByte(0x5E);
        I2CWaitAck();
    
        I2CSendByte(val);
        I2CWaitAck();
        I2CStop();
    }

    读取函数我们一般用不到,因为我们只需要写入控制它的阻值就行了,并不需要读取现在阻值多少。比赛的时候也几乎很少遇到,但是最好还是学一下,也可以通过读取函数来判断是否成功写入,防止数据干扰。比方说可以写完之后读取一下,判断写的对不对,如果不对就再写一次。但是比赛中不需要这样。

    u8 MCP4017_Read(void)
    {
        u8 val;
    
        I2CStart();
        I2CSendByte(0x5F);
        I2CWaitAck();
    
        val=I2CReceiveByte();
        I2CSendNotAck();
        I2CStop();
    
        return val;
    }

     还有一点就是,MCP4017两次写入之间是不需要延时的,不同于EEPROM。

    三、ADC采样分压电压

    MCP4017的W端接了一个PB14的管脚,并接在了ADC1的输入口上,所以我们可以用PB14来采样分压电压。

    进入CubeMX开始配置引脚。可以看到PB14可以设置为ADC1的5通道,与PB12共用一个ADC,但是通道不同。我们把它设置为单端模式直接采集电压就行了。如果这部分内容还不清楚的话可以翻到之前的内容看一下ADC的讲解。

    我们需要注意的是,ADC1现在有两个通道,那我们怎么告诉STM32现在要采集哪个通道呢?这边就有一个内容要在CubeMX上配置的,在ADC1的Configuration中有一个Number of Conversion也就是ADC转换的数量,这里要改成2。改完后下面有个Rank就自动变成2个了,并自动打开Scan conversion mode扫描转换模式,它会先转换一个通道的输入后再去转换另一个通道,转化的顺序就是由Rank决定,Rank1就是第一个被转换的通道,我们可以在Rank里面选择,并且这里我们可以调整它的采样速度Sampling Time(采样时间)。注意,多通道采集的时候,采样速度最好不要太快,否则会容易出错,所以我们可以把采样速度调的慢一点,这里我们调640.5个周期。

    再把Rank1设置为通道5,Rank2设置为通道11。

    生成代码,我们可以看到ADC1的初始化定义里面已经有了要转换的通道数量为2,和不同Rank对应的通道。

    我们已经有了之前的ADC的程序,现在在此基础上增加ADC1通道5的内容。(如果没有的话就自己写一下,配置ADC的教程在前面有,写完记得初始化一下,有的时候程序写完了发现运行不对有可能就是忘了初始化)

    我们定义一个变量volt_mcp来表示通过mcp采集到的电压值。

    因为我们定义的Rank1是采集mcp,也就是先采集mcp再采集R37,所以我们是在采集R37的程序前面添加采集mcp的程序。

    u16 adc1_val,adc2_val;
    float volt_r37,volt_r38,volt_mcp;
    void ADC_Process(void)
    {
    	//rank1
    	HAL_ADC_Start(&hadc1);
    	volt_mcp = HAL_ADC_GetValue(&hadc1)/4095.0f*3.3f;
    	//rank2
    	HAL_ADC_Start(&hadc1);
    	adc1_val = HAL_ADC_GetValue(&hadc1);
    	volt_r37 = adc2_val/4095.0f*3.3f;
    	
    	HAL_ADC_Start(&hadc2);
    	adc2_val = HAL_ADC_GetValue(&hadc2);
    	volt_r38 = adc1_val/4095.0f*3.3f;
    }

    这样就完成了ADC1先采集通道5,再采集通道11的过程,需要注意的是,我们并不需要手动切换采集通道,只需要再调用一次HAL_ADC_Start()函数就可以自动切换通道了,这是因为我们之前配置了扫描转换,正因如此,我们在编写程序的时候一定要注意采样的顺序!

    我们可以调用一下前面写好的MCP4017_Write(),然后写入0x3F,也就是中位点。设置好电阻后在主循环中调用ADC采样函数就可以看一下现在的分压电压了。

    int main()
    {
        MCP4017_Write(0x3F);
        while(1);
        {
            ADC_Process();
        }
    }

    我们可以写个LCD显示函数来看一下此时volt_mcp的值,这里就跳过了。显示出来的值应该是2.7左右。如果输入0x7F的话,输出应该是2.95左右。

    如果之前没有现成的ADC程序的话,我们总结一下利用ADC1双通道采集的程序设计步骤:

  • 用给的“模版”生成代码
  • 设置ADC相关的GPIO为输入模式,并设置成单端模式。(PB15、PB12、PB14)
  • 设置ADC1的转换的通道数(number of conversion)为2,并设置Rank和采集速度。
  • 添加ADC相关的HAL库驱动文件
  • 包含ADC的头文件,并初始化ADC(如果是移植代码的话要初始化外设时钟)
  • 利用HAL_ADC_Start和HAL_ADC_GetValue编写程序(双通道要调用两次Start)
  • 总结

    这节课主要是学习如何使用MCP4017数字电位器,它需要通过I2C总线进行通信,利用I2C向其内部写入数据从而控制它的内部电阻大小,并且外部接了一个分压电路,并把分压电压作为ADC的输入采样,从而转化成数字量被我们读取。所以这一节的内容需要结合I2C总线和ADC这两部分内容一起来学习的。其中最重要的内容是MCP4017读写函数的编写和ADC的双通道采集这两部分内容,比赛时是需要自己编写的,需要牢记。此外,还需要记得ADC的采样Rank和采样速度。

    作者:我不是板神

    物联沃分享整理
    物联沃-IOTWORD物联网 » 嵌入式开发学习笔记:使用STM32G431和HAL库控制数字电位器MCP4017

    发表评论