专业版
#立创电赛#基于STM32和SHT40的桌面低功耗可充电温湿度计

创建时间:3个月前

描述

<h1>目录</h1> <ul> <li><a href="#1%E4%BB%8B%E7%BB%8D" target="_blank">1、介绍</a></li> <li><a href="#2%E7%A1%AC%E4%BB%B6%E9%83%A8%E5%88%86" target="_blank">2、硬件部分</a> <ul> <li><a href="#21%E7%94%B5%E6%BA%90%E9%83%A8%E5%88%86" target="_blank">2.1电源部分</a></li> <li><a href="#22-%E6%B8%A9%E6%B9%BF%E5%BA%A6%E6%98%BE%E7%A4%BA" target="_blank">2.2 温湿度显示</a></li> <li><a href="#23-stm32%E5%A4%96%E5%9B%B4%E7%94%B5%E8%B7%AF" target="_blank">2.3 STM32外围电路</a></li> <li><a href="#24-sht40%E6%A8%A1%E5%9D%97" target="_blank">2.4 SHT40模块</a></li> </ul></li> <li><a href="#3%E8%BD%AF%E4%BB%B6%E9%83%A8%E5%88%86" target="_blank">3、软件部分</a> <ul> <li><a href="#31-%E8%BD%AF%E4%BB%B6%E5%AE%9E%E7%8E%B0" target="_blank">3.1 软件实现</a></li> <li><a href="#311%E6%95%B0%E7%A0%81%E7%AE%A1%E6%98%BE%E7%A4%BA%E6%95%B0%E5%AD%97" target="_blank">3.1.1数码管显示数字</a></li> <li><a href="#312-adc%E9%87%87%E9%9B%86%E7%94%B5%E6%B1%A0%E7%94%B5%E5%8E%8B" target="_blank">3.1.2 ADC采集电池电压</a></li> <li><a href="#313-%E6%A8%A1%E6%8B%9Fi2c%E8%BD%AF%E4%BB%B6i2c" target="_blank">3.1.3 模拟I2C(软件I2C)</a></li> <li><a href="#314-%E4%B8%AD%E6%96%AD%E5%94%A4%E9%86%92" target="_blank">3.1.4 中断唤醒</a></li> <li><a href="#32-%E8%BD%AF%E4%BB%B6i2c%E8%AF%A6%E8%A7%A3" target="_blank">3.2 软件I2C详解</a></li> <li><a href="#321-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E7%94%A8%E8%BD%AF%E4%BB%B6i2c" target="_blank">3.2.1 为什么要用软件I2C?</a></li> <li><a href="#322-i2c%E9%80%9A%E4%BF%A1%E5%8E%9F%E7%90%86%E8%A7%A3%E9%87%8A" target="_blank">3.2.2 I2C通信原理解释</a></li> <li><a href="#323-i2c%E9%80%9A%E4%BF%A1%E8%BF%87%E7%A8%8B" target="_blank">3.2.3 I2C通信过程</a></li> <li><a href="#324-i2c%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95" target="_blank">3.2.4 I2C代码实现方法</a></li> <li><a href="#325-%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B8%A9%E6%B9%BF%E5%BA%A6%E4%BC%A0%E6%84%9F%E5%99%A8%E7%9A%84i2c%E9%80%9A%E4%BF%A1" target="_blank">3.2.5 实现与温湿度传感器的I2C通信</a></li> <li><a href="#33-%E4%BC%98%E5%8C%96%E7%A8%8B%E5%BA%8F%E9%99%8D%E4%BD%8E%E5%8A%9F%E8%80%97%E7%9A%84%E6%96%B9%E6%B3%95" target="_blank">3.3 优化程序降低功耗的方法</a></li> <li><a href="#34-%E7%A8%8B%E5%BA%8F%E7%83%A7%E5%BD%95%E6%AD%A5%E9%AA%A4" target="_blank">3.4 程序烧录步骤</a></li> </ul></li> <li><a href="#4-%E6%95%85%E9%9A%9C%E6%8E%92%E9%99%A4" target="_blank">4 故障排除</a></li> <li><a href="#5-%E9%A1%B9%E7%9B%AE%E5%B1%9E%E6%80%A7" target="_blank">5 项目属性</a></li> <li><a href="#6-%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE" target="_blank">6 开源协议</a></li> <li><a href="#7-%E5%A4%A7%E8%B5%9Blogo%E9%AA%8C%E8%AF%81" target="_blank">7 大赛LOGO验证</a></li> <li><a href="#8-%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91" target="_blank">8 演示视频</a></li> <li><a href="#%E5%86%99%E5%9C%A8%E5%90%8E%E9%9D%A2" target="_blank">写在后面</a></li> <li><a href="#%E5%8F%82%E8%80%83" target="_blank">参考</a></li> </ul> <h1>1、介绍</h1> <p>在日常生活中,无论是家庭、办公室还是工作室,适宜的温湿度环境对于人体舒适度、工作效率乃至家居设备的保养都至关重要。然而,传统的温湿度计存在精度不足、需频繁更换一次性电池等问题,既不方便也不环保。</p> <p>于是,我萌生了设计一款集低功耗、高精度、低成本于一身的桌面温湿度计的想法。这款产品的核心优势在于它巧妙地平衡了性能与成本,提供一个既实用又环保的温湿度解决方案。它具有以下特点:</p> <p>(1)<span style="font-size:18px;color:#00CED1">低功耗:</span>考虑到长期使用的需求,本次设计选择低功耗的硬件组件和优化的软件算法。<span style="font-size:16px;color:#1E90FF">STM32G030K6T6</span>作为MCU,以其出色的能效比和丰富的外设资源,成为这款传感器的理想之选。通过精细的电源管理和休眠模式设计,能大幅度降低能耗,延长单次充电后的使用时间。</p> <p>(2)<span style="font-size:18px;color:#FF7F50">高精度测量:</span>为了确保温湿度数据的准确性,本次设计选用了<span style="font-size:16px;color:#1E90FF">瑞士盛思锐SHT40</span>作为温湿度传感器。SHT40以其卓越的测量精度和稳定性著称,能够在广泛的温度和湿度范围内提供可靠的数据。</p> <p>(3)<span style="font-size:18px;color:#9932CC">低成本:</span>通过精选高性价比的元器件和优化设计方案,温湿度传感器成本整体控制在<span style="font-size:18px;color:red"><strong>30元以内</strong> </span>(不含运费,立创商城价格)</p> <p>(4)<span style="font-size:18px;color:#228B22">可充电:</span>采用<span style="font-size:16px;color:#1E90FF">18650锂电池</span>作为电源,不仅避免了频繁更换电池带来的不便,还减少了废旧电池对环境的污染。</p> <h1>2、硬件部分</h1> <h2>2.1电源部分</h2> <p>电源框图如下图所示:</p> <p><img src="//image.lceda.cn/oshwhub/d7a36730e2bb4886bbc558f08b0280a4.png" alt="电源框图.png"> <span style="font-size:18px;color:#228B22">设计思路</span></p> <p>官方设计是使用的两节1.5V干电池,由于需要经常更换电池,废弃电池也会造成污染,因此考虑使用锂电池供电。</p> <p>经过查阅数据手册得知,STM32G030K6T6供电范围 2V-3.6V、SHT40 1.08V-3.6V、而锂电池满电时能有4V以上,超过了它们允许的最大允许的电源电压,因此需要采用先升压后降压的方式,稳定电压在3.3V。</p> <p>因为要兼顾整机功耗和成本,经过挑选,<span style="font-size:17px;color:#2E86C1">使用MT3608B做升压、TLV70233做降压。</span></p> <p>其中,MT3608B在电流小于100mA时的效率约92%。其输出电压是使用电阻分压反馈方式,VOUT=(1+R2/R1) * VREF。手册里写VREF=0.6V,我取R2=91KΩ,R1=13KΩ得到VOUT=4.8V。这里两个分压电阻大一点好,这样流过它们的电流小,它们所耗的功率也会变小。这里的电感4.7uH是按芯片手册来的,建议选择一个等效直流电阻更低的电感,这样也能提高效率。这里的续流二极管必选肖特基二极管,建议用<strong>SS14</strong>就行,我用SS34是因为手头上正好有这个。</p> <p><img src="//image.lceda.cn/oshwhub/4ce513f007d343d6973a5907af1475a8.png" alt="image.png"></p> <p><span style="font-size:18px;color:#228B22">Layout注意事项</span></p> <p>输入端的电容C15要尽可能贴近DC-DC芯片放置,而输出端C14要与芯片地的回路最短。</p> <p><img src="//image.lceda.cn/oshwhub/ad1d241ca12e4e72b22605ca071602a5.png" alt="image.png"></p> <p>两个反馈电阻要远离电感,避免高频干扰</p> <p><img src="//image.lceda.cn/oshwhub/04b6992a1c1d4f48b7a594f44fab971c.png" alt="image.png"></p> <p>LDO这边,输入和输出的滤波电容需要贴近LDO放置</p> <p><img src="//image.lceda.cn/oshwhub/068725ed591f4acc997d9444cdeb3c73.png" alt="image.png"></p> <h2>2.2 温湿度显示</h2> <p><span style="font-size:18px;color:#228B22">设计思路</span></p> <p>温湿度分别显示在两片共阴极数码管上。数码管通过三个74HC595移位寄存器控制。移位寄存器的功能框图如下图所示</p> <p><img src="//image.lceda.cn/oshwhub/efd3040e4f1342809d7c84c93f7fc6d2.png" alt="image.png"> 两个数码管的阴极总共是6个阴极,全部连接在其中一个SN74HC595上,通过这一个595芯片,可以指定某一个位导通,同时,两个数码管的阳极,又分别连接在另外两个595芯片上,通过这两个芯片配合,就可以实现单个位显示数据。</p> <p><img src="//image.lceda.cn/oshwhub/68ccba02114c4832beb40cbe02da064d.png" alt="image.png"></p> <p><span style="font-size:18px;color:#228B22">Layout注意事项</span></p> <p>电源滤波电容靠近移位寄存器VCC放置</p> <p><img src="//image.lceda.cn/oshwhub/fb11a92beb1541ebaad9f7522525f645.png" alt="image.png"></p> <h2>2.3 STM32外围电路</h2> <p><span style="font-size:18px;color:#228B22">(1)ADC电压采集</span></p> <p>使用了两个10KΩ薄膜电阻串联分压,其精度误差为±0.1%,可以基本保证电压采样的准确性。采样点在中间位置,最终需要在程序内乘以2,才能得到近似准确的电池电压。例如由程序ADC转换后的电压为1.92V,则实际电压为3.84V。旁路增加一个100nF电容接地也是为了消除一些干扰。</p> <p><img src="//image.lceda.cn/oshwhub/593ff430be6144c89a8d293445f6fb45.png" alt="image.png"></p> <p><span style="font-size:18px;color:#228B22">(2)按键中断唤醒</span></p> <p>使用一个按键开关实现,一端接STM32的WAKE引脚(需要自己定义),一端接地。当按下后,WAKE引脚从高电平到低电平转换,就产生了下降沿,只需要读到这个下降沿即可执行后续的中断操作,这在后面软件设计部分会细说。 <img src="//image.lceda.cn/oshwhub/4d18183e89d04819897021dcd4aaaa7d.png" alt="image.png"></p> <h2>2.4 SHT40模块</h2> <p><span style="font-size:18px;color:#228B22">设计思路</span></p> <p>立创商城中可以买到<a href="https://item.szlcsc.com/24072575.html?fromZone=s_s__%2522sht40%2520with%2522" target="_blank">SHT40模块</a>,它带有一个滤波电容和传感器,可以发现少了两个I2C通信线上的上拉电阻,我们需要在原理图加上</p> <p><img src="//image.lceda.cn/oshwhub/3b98d282c0204816b315e680126668a9.png" alt="image.png"></p> <p><img src="//image.lceda.cn/oshwhub/29b98c74e41b457fbeb9fe15565f2340.png" alt="image.png"></p> <p><span style="font-size:18px;color:#228B22">原理图注意事项</span></p> <p><span style="font-size:17px;color:red">请注意原理图中SCL与SDA的位置</span>,一定要根据模块的引脚功能仔细对照!我设计的板子是将传感器朝上放置,如果你想改为朝下放置,则需要改原理图,将引脚镜像对调。</p> <p>传感器模块各引脚定义如下图所示 <img src="//image.lceda.cn/oshwhub/cef5f89c1a1a4ddd8f3ad54535a93963.jpg" alt="ABEEB11A2AFD5237CEABF774795440C9.jpg"></p> <p><span style="font-size:18px;color:#228B22">Layout注意事项</span></p> <p>SHT40这个传感器真的非常非常灵敏,在布局时需要让模块尽量远离发热器件,如充电模块、STM32等,因此我将其布局在了PCB的右上角,底下不铺铜,以减少其他器件散热对它造成的影响。在阻焊颜色选择上,我选用吸热较少的白色,同样是为了减少外界对传感器的影响。</p> <h1>3、软件部分</h1> <h2>3.1 软件实现</h2> <p>部分软件代码参考<a href="https://www.yuque.com/wldz/jlceda/ycxrhmcyxkvomgm1" target="_blank">官方项目文档</a>。 代码已开源并上传到<strong>附件</strong>。</p> <h3>3.1.1数码管显示数字</h3> <p>数码管本身不含有任何控制单元,它只是由几个有序排列的LED组成的器件,所以我们需要控制移位寄存器74HC595,通过它来驱动数码管显示数字。74HC595的时序图如下图所示</p> <p><img src="//image.lceda.cn/oshwhub/1dd08b440d47454aa2bbf227c6235b55.png" alt="image.png"></p> <p>从时序图可以知道74HC595的使用流程为:</p> <p>①拉高SCLR(10脚)。如果不用,设计原理图时可以直接拉高。</p> <p>②控制SI(14引脚)、SCK(11脚)把移位寄存器的值赋好(使用8个上升沿)。</p> <p>③给RCK(12引脚)一个上升沿。</p> <p>④拉低G(13脚)。</p> <p>首先定义函数<code>SN74HC595_Send_Data</code>,它有两个参数:<code>sn_num</code>: 表示选择哪个数码管或设备,值有<code>SN_LED1</code>、<code>SN_LED2</code>和<code>SN_DIG</code>;<code>sendValue</code>: 需要发送的数据。 这里sn_num一共有三种情况,只挑值为SN_LED1的来讨论,其他两个内容都是类似的</p> <p><img src="//image.lceda.cn/oshwhub/e59cbd742c5a4f7a8db1f7a7046f0f0d.png" alt="image.png"></p> <p>①选择第一个数码管 (<code>SN_LED1</code>):</p> <pre><code class="language-c"> if(sn_num == SN_LED1)</code></pre> <p>②使用<code>for</code>循环遍历8次(因为74HC595是8位移位寄存器):</p> <pre><code class="language-c"> for(i = 0; i &lt; 8; i++)</code></pre> <p>③检查<code>sendValue</code>的每一位,如果该位是1,则设置相应的引脚为高电平,否则为低电平:</p> <pre><code class="language-c"> if(((sendValue &lt;&lt; i) &amp; 0x80) != 0)</code></pre> <p>④产生一个SCLK上升沿,时钟信号用于将数据从串行输入移位到寄存器中:</p> <pre><code class="language-c"> HAL_GPIO_WritePin(LED1_SCLK_GPIO_Port, LED1_SCLK_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED1_SCLK_GPIO_Port, LED1_SCLK_Pin, GPIO_PIN_SET);</code></pre> <p>⑤在循环结束后,产生一个RCLK上升沿,用于将移位寄存器中的数据锁存到输出寄存器中:</p> <pre><code class="language-c"> HAL_GPIO_WritePin(LED1_RCLK_GPIO_Port, LED1_RCLK_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED1_RCLK_GPIO_Port, LED1_RCLK_Pin, GPIO_PIN_SET);</code></pre> <h3>3.1.2 ADC采集电池电压</h3> <p>前面硬件设计时提到过,用两个10KΩ薄膜电阻实现分压,STM32采集中间点的电压值,将其转换为ADC值,然后经过一系列运算最终得到电压的准确值。</p> <p>首先要在STM32CubeMX中配置好ADC引脚,点击PB1引脚,选择ADC1_IN9</p> <p><img src="//image.lceda.cn/oshwhub/2d4f735d9d2d4266b5c3a84ee07c950c.png" alt="image.png"></p> <p>来到NVIC中,使能ADC1中断</p> <p><img src="//image.lceda.cn/oshwhub/f67d9ba84c8e4da7a5cdbfd38e09b841.png" alt="image.png"></p> <p>再点进Code Generation,按如图所示勾选即可</p> <p><img src="//image.lceda.cn/oshwhub/aa3ce75db34843f5a580edd64aa639f5.png" alt="image.png"></p> <p>最后点击GENERATE CODE,打开Keil项目,开始编写ADC代码</p> <p>这里将ADC代码拆分成三块讲解</p> <p><img src="//image.lceda.cn/oshwhub/739d22c960ba409a9f219447c04aa907.png" alt="image.png"></p> <p>①调用HAL库函数,初始化、启动ADC转换,并等待其转换完成</p> <p>②判断是否获取到数值,由于STM32G030K6T6是12bit ADC,也就是会有2的12次方个ADC数值,ADC数值从0到4095。将其归一化处理,并乘上STM32的输入电压。输入电压我用3.324V是拿万用表测得的,不需要太精确的话写3.3就可以了。归一化后的数据即为一半的电池电压值,变量为Data,比如说1.92V。</p> <p>③由于需要显示在数码管上,我们把Data放大100倍再乘以2,这样就得到了一个整数,比如384,可以用取十进制数每一位的值的方法,得到三个数:3 8 4,并赋值给<code>device_paramter</code>结构体中的成员Voltage数组 在<code>main.h</code>头文件中,<code>device_paramter</code>结构体定义如下</p> <pre><code class="language-c">struct DEVICE_PARAMTER { volatile uint8_t KeyStatus; volatile uint8_t sleepStatus; uint16_t Temp; uint16_t Humi; uint8_t Voltage[3]; };</code></pre> <p>在gpio.c文件中,编写了一个显示电压的函数,这样就方便一键调用了</p> <pre><code class="language-c">void ShowVoltage(){ SN74HC595_Send_Data(SN_DIG,0xFE); SN74HC595_Send_Data(SN_LED1,sgh_value[device_paramter.Voltage[0]]|0x80); SysCtlDelay(1000); SN74HC595_Send_Data(SN_LED1,0x00); //消影,防止错位 SN74HC595_Send_Data(SN_DIG,0xFD); SN74HC595_Send_Data(SN_LED1,(sgh_value[device_paramter.Voltage[1]])); SysCtlDelay(1000); SN74HC595_Send_Data(SN_LED1,0x00); //消影,防止错位 SN74HC595_Send_Data(SN_DIG,0xFB); SN74HC595_Send_Data(SN_LED1,sgh_value[device_paramter.Voltage[2]]); SysCtlDelay(1000); SN74HC595_Send_Data(SN_LED1,0x00); //消影,防止错位 } </code></pre> <h3>3.1.3 模拟I2C(软件I2C)</h3> <p>由于我在设计时把SCL和SDA画反(目前EDA编辑器里的板子已更正,设计为传感器模块正面向上插入),导致我需要飞线或者用软件I2C解决通信问题。经过查阅大量资料、参考代码,我自行修改并匹配了本项目所使用的传感器和MCU,实现了使用HAL库模拟I2C通信。如果你和我一样无法直接使用硬件I2C,别着急,只需要修改<code>softiic.h</code>内SDA和SCL对应的GPIO即可。</p> <p><img src="//image.lceda.cn/oshwhub/cb2a448ff7614b4296de18ff4aa24d00.png" alt="image.png"></p> <p><span style="font-size:17px;color:red">由于软件I2C篇幅较长,所以放到第3.2节细讲 </span></p> <h3>3.1.4 中断唤醒</h3> <p><span style="font-size:18px;color:#458FC3">(1)STM32CubeMX参数设置</span></p> <p>打开STM32CubeMX,设置PB5为GPIO_EXTI5,GPIO模式选择“下降沿触发检测的外部中断”,GPIO上拉,命名标签为WAKE_KEY</p> <p><img src="//image.lceda.cn/oshwhub/e2305d0df99d48faa91b1090bd424ad6.png" alt="image.png"></p> <p>在NVIC中,设置EXTI line 4 to 15 interrupts使能并将优先级设为1;然后进到Code generation中,勾选对应的生成代码等复选框</p> <p><img src="//image.lceda.cn/oshwhub/71db7f77a5794cf4bb67a21835cfa70e.png" alt="image.png"> <img src="//image.lceda.cn/oshwhub/2a0fb7afe40d4a8f94f2e4f41a7ba60b.png" alt="image.png"></p> <p>在Timers- TIM14中使能TIM14 全局中断</p> <p><img src="//image.lceda.cn/oshwhub/482fd0dd4f4147cd8f93349a9fdf30ac.png" alt="image.png"></p> <p>OK,点击右上角的生成代码,打开Keil,编辑代码。</p> <p><span style="font-size:18px;color:#458FC3">(2)中断代码</span></p> <p>①本段程序在<code>tim.c</code>中,是回调函数。当定时器被触发时,HAL库自动调用该段函数。 函数主要做了两件事:显示温湿度、显示电池电压。变量<code>flag</code>用于计时以及判断是否该调整休眠标志<code>sleep_flag</code>。</p> <pre><code class="language-c">void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM14) { HAL_TIM_Base_Stop_IT(&amp;htim14); updata_flag++; if(updata_flag &lt;= 1000) { ShowNum(1,1,(device_paramter.Temp/100)); ShowNum(1,2,(device_paramter.Temp / 10 % 10)); ShowNum(1,3,device_paramter.Temp%10); ShowNum(2,1,(device_paramter.Humi/100)); ShowNum(2,2,(device_paramter.Humi / 10 % 10)); ShowNum(2,3,device_paramter.Humi%10); } else if(updata_flag &lt;= 2000) { ShowVoltage(); } else { updata_flag = 0; sleep_flag++; } __HAL_TIM_SetCounter(&amp;htim14,0); if(sleep_flag >= 1) { sleep_flag = 0; device_paramter.sleepStatus = 1; SN74HC595_Send_Data(SN_DIG,0xFF); SN74HC595_Send_Data(SN_LED1,0x00); SN74HC595_Send_Data(SN_LED2,0x00); } else{ HAL_TIM_Base_Start_IT(&amp;htim14); } } }</code></pre> <p>②本段程序在<code>main.c</code>的<code>while(1)</code>循环中,这里把SHT40温湿度采集与ADC电压采集的代码删掉,只保留该节要讲的内容。</p> <p>循环一直判断按键的状态,当读取到PB5引脚为低电平(GPIO_PIN_REST)时,进入下一级循环,然后执行<code>HAL_TIM_Base_Start_IT()</code>启动定时器,它会自动调用上面的<code>HAL_TIM_PeriodElapsedCallback</code>,待回调函数内执行完毕,睡眠标志到来时,将退出回调函数,回到这一段代码,继续把休眠标志和按下标志清除掉,MCU进入休眠,可以进行下一次唤醒。</p> <pre><code class="language-c"> if(device_paramter.KeyStatus == KEY_SHAKE_STATE) { HAL_Delay(10); if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) == GPIO_PIN_RESET) { while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) == GPIO_PIN_RESET); /* SHT40采集温湿度 */ /* ADC采集电压 */ HAL_TIM_Base_Start_IT(&amp;htim14); //开始定时器,显示数据 device_paramter.sleepStatus = 0; //清除休眠标志 device_paramter.KeyStatus = KEY_NO_PRESS; //清除按下标志 } } else if(device_paramter.sleepStatus == 1) //显示结束,进入休眠 { HAL_SuspendTick(); //暂停滴答定时器,防止通过滴答定时器中断唤醒 HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); //进入停止模式 } </code></pre> <h2>3.2 软件I2C详解</h2> <h3>3.2.1 为什么要用软件I2C?</h3> <p>I2C通信协议是本项目的关键所在,SHT40支持I2C通信,只有搞懂I2C通信的全过程,才能给SHT40传感器发送读数据命令、正确接收温湿度数据。据说STM32的硬件I2C存在bug,再加上本人被迫需要学习软件I2C,因此花了一天的时间来学习调试,在此记录、解析一下I2C的通信过程及其如何实现获取SHT40的温湿度数据的。</p> <h3>3.2.2 I2C通信原理解释</h3> <p><span style="font-size:18px;color:#458FC3">(1)空闲状态</span></p> <p>当SCL和SDA都为高电平时</p> <p><img src="//image.lceda.cn/oshwhub/f49c4d4539ba4ae280e26325d76a9100.png" alt="image.png"> <span style="font-size:18px;color:#458FC3">(2)起始、结束条件</span></p> <p>起始条件:当SCL为高电平时,SDA有一个下降沿</p> <p>结束条件:当SCL为高电平时,SDA有一个上升沿</p> <p><img src="//image.lceda.cn/oshwhub/5f66459d97ef4063871a48ae95ff4aa2.png" alt="image.png"></p> <p><span style="font-size:18px;color:#458FC3">(3)从机地址、寄存器地址</span></p> <p>I2C通信就如同广播找人一样,你叫到某个人的名字(从机的地址),它才会给你回应。因此,需要在开始条件后紧跟着发送一个从机地址。从机地址由7bit组成,第8位是读写标志位,0表示写,1表示读</p> <p><img src="//image.lceda.cn/oshwhub/74ed149d8241475a8fcddff855c927b4.png" alt="image.png"></p> <p>举例:当你广播找人,找到了那个人(从机),他回应你了(ACK),你要他口袋里(寄存器地址)的钥匙(数据) <span style="font-size:18px;color:#458FC3">(4)字节格式</span> 开始条件之后,每8个bit为一个字节传输,当SCL为高电平时,SDA也是高电平,此时代表逻辑1;SCL为高电平,SDA是低电平时,代表逻辑0。 <img src="//image.lceda.cn/oshwhub/ae4056680f464d6dbd25bee6db7f7a98.png" alt="image.png"></p> <p><span style="font-size:18px;color:#458FC3">(5)应答位</span></p> <p>I2C的数据都是以8bit传送的,发送器每发送一个字节,就在时钟脉冲9期间释放数据线SDA,由接收器反应一个应答信号。当应答信号为低电平时,规定为有效应答位(ACK),表示接收器已经成功接收该字节。当应答信号为高电平时,规定为非应答位(NACK),表示接收器接收该字节没有成功。</p> <p>举个例子:这就像是你给别人说你的电话号码,你一次性把11位电话号说给他,他可能一下子记不住,你分成三段,第一段说139,他说 好的(有效应答位ACK);第二段你说1234,他说 嗯(有效应答位ACK);第三段你说5678,这时一辆大车鸣笛经过,淹没了你的声音,他就没听清,没回应你(非应答位NACK),此时你看他没反应,你就要再说一遍5678,他回答好的(有效应答位ACK)。</p> <h3>3.2.3 I2C通信过程</h3> <p><span style="font-size:18px;color:#458FC3">(1)主机写入数据的流程</span></p> <p><img src="//image.lceda.cn/oshwhub/595c68ecaeea41babeacd82a4d441902.png" alt="image.png"></p> <p><span style="font-size:18px;color:#458FC3">(2)主机读取数据的流程</span></p> <p><img src="//image.lceda.cn/oshwhub/7434aab7cb504071a7be14b4d3b9fb91.png" alt="image.png"></p> <h3>3.2.4 I2C代码实现方法</h3> <p>有了上述理论基础,就可以开始编写代码了。这里我单独创建了一个C源文件叫<code>softiic.c</code>,下面将逐个函数讲解其作用。</p> <p><span style="font-size:18px;color:#458FC3">(1)使用HAL库时自定义的delay_us函数</span></p> <p>由于HAL库官方给的HAL_Delay是以ms级别的,我们要产生模拟I2C信号需要有延迟,通常是5us。<a href="https://shequ.stmicroelectronics.cn/thread-634303-1-1.html" target="_blank">ST论坛</a>有大佬给出了解决方法,这里就直接照搬了。注意修改时钟主频为STM32G030K6T6的64MHz</p> <pre><code class="language-c">#define CPU_FREQUENCY_MHZ 64 // STM32时钟主频 MHz void delay_us(__IO uint32_t delay) { int last, curr, val; int temp; while (delay != 0) { temp = delay > 900 ? 900 : delay; last = SysTick->VAL; curr = last - CPU_FREQUENCY_MHZ * temp; if (curr >= 0) { do { val = SysTick->VAL; } while ((val &lt; last) &amp;&amp; (val >= curr)); } else { curr += CPU_FREQUENCY_MHZ * 1000; do { val = SysTick->VAL; } while ((val &lt;= last) || (val > curr)); } delay -= temp; } }</code></pre> <p><span style="font-size:18px;color:#458FC3">(2)配置切换SDA引脚输入或输出模式的函数</span></p> <p>使用HAL库的配置方法。注意,输出模式为开漏(OD)输出</p> <pre><code class="language-c">static void SDA_OUT(void){ GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT; SOFT_IIC_GPIO_STRUCT.Mode = GPIO_MODE_OUTPUT_OD; SOFT_IIC_GPIO_STRUCT.Pin = SDA_PIN; SOFT_IIC_GPIO_STRUCT.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(SDA_PORT, &amp;SOFT_IIC_GPIO_STRUCT); } static void SDA_IN(void) { GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT; SOFT_IIC_GPIO_STRUCT.Mode = GPIO_MODE_INPUT; SOFT_IIC_GPIO_STRUCT.Pin = SDA_PIN; SOFT_IIC_GPIO_STRUCT.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(SDA_PORT, &amp;SOFT_IIC_GPIO_STRUCT); } </code></pre> <p><span style="font-size:18px;color:#458FC3">(3)I2C延迟时间定义</span></p> <p>前面有了自定义函数<code>delay_us</code>,这里我们填5,意味着每次调用函数,延迟5us</p> <pre><code class="language-c">void IIC_Delay(void) { delay_us(5); }</code></pre> <p><span style="font-size:18px;color:#458FC3">(4)I2C开始信号</span></p> <p>先将SDA设置为输出模式,SDA和SCL同时为高电平(空闲状态),经过一点延时,SDA拉低,SCL过5us后再拉低,完成开始信号的设置。</p> <pre><code class="language-c">void IIC_Start(void) { SDA_OUT(); SDA_HIGH(); SCL_HIGH(); IIC_Delay(); SDA_LOW(); IIC_Delay(); SCL_LOW(); }</code></pre> <p><span style="font-size:18px;color:#458FC3">(5)I2C结束信号</span></p> <p>先将SDA设置为输出模式,SDA和SCL同时为低电平,经过一点延时,SCL先拉高,SDA过5us后再拉高,完成结束信号的设置。</p> <pre><code class="language-c">void IIC_Stop(void) { SDA_OUT(); SCL_LOW(); SDA_LOW(); IIC_Delay(); SCL_HIGH(); IIC_Delay(); SDA_HIGH(); IIC_Delay(); }</code></pre> <p><span style="font-size:18px;color:#458FC3">(6)应答位</span></p> <p>这里主要是IIC_Wait_Ack函数需要讲一下,IIC_Ack和IIC_NAck是模拟主机发送应答或非应答信号,看着前面I2C的定义图应该很清楚,这里就不逐行啰嗦了。</p> <p>IIC_Wait_Ack函数中,要将SDA设置为输入模式,以便于读取SDA信号线上电平的变化,其中<code>READ_SDA()</code>是由宏定义为 <code>HAL_GPIO_ReadPin(SDA_PORT, SDA_PIN)</code>,如果高电平,则返回值为1;若低电平,返回值为0。回顾一下,ACK是要求从机有一个拉低SDA的操作,即<code>READ_SDA()</code>返回值为0,方可跳出while循环,继续下面拉低SCL的操作。</p> <p>在循环内,<code>wait</code>变量是用来判断是否超时,只要超过200个周期,就判定超时,停止I2C通信.</p> <pre><code class="language-c">uint8_t IIC_Wait_Ack(void) { uint8_t wait; SDA_IN(); IIC_Delay(); SCL_HIGH(); IIC_Delay(); while (READ_SDA()) { wait++; if (wait > 200) { IIC_Stop(); return 1; } } SCL_LOW(); return 0; } void IIC_Ack(void) { SCL_LOW(); SDA_OUT(); SDA_LOW(); IIC_Delay(); SCL_HIGH(); IIC_Delay(); SCL_LOW(); } void IIC_NAck(void) { SCL_LOW(); SDA_OUT(); SDA_HIGH(); IIC_Delay(); SCL_HIGH(); IIC_Delay(); SCL_LOW(); }</code></pre> <p><span style="font-size:18px;color:#458FC3">(7)I2C发送一个字节</span></p> <p>这里就不一行行去解读了,主要看一下for循环里面的内容在干什么。</p> <p>①因为一个字节包含8位,而I2C通信中数据是以位为单位传输的。因此,<code>for (uint8_t i = 0; i &lt; 8; i++)</code> ,从0到7遍历一个字节的每一位。</p> <p>②将<code>byte</code>向左移动<code>i</code>位,这样原本的第<code>i</code>位就移动到了最高位(即第7位,对应于十六进制的<code>0x80</code>)。然后,使用<code>&amp;</code>操作符与<code>0x80</code>进行按位与操作。如果结果为非零(即<code>true</code>),说明<code>byte</code>的第<code>i</code>位是1;如果结果为0(即<code>false</code>),则第<code>i</code>位是0。</p> <p>③设置SDA线的电平:根据上一步的结果,通过<code>SDA_HIGH()</code>和<code>SDA_LOW()</code>宏或函数来设置SDA线的电平。如果当前位是1,则调用<code>SDA_HIGH()</code>将SDA线设置为高电平;如果当前位是0,则调用<code>SDA_LOW()</code>将SDA线设置为低电平。这样,就按照<code>byte</code>变量的值,一位一位地将数据通过SDA线发送出去。</p> <pre><code class="language-c">void IIC_Send_Byte(uint8_t byte) { SDA_OUT(); SCL_LOW(); for (uint8_t i = 0; i &lt; 8; i++) { if ((byte &lt;&lt; i) &amp; 0x80) { SDA_HIGH(); } else { SDA_LOW(); } IIC_Delay(); SCL_HIGH(); IIC_Delay(); SCL_LOW(); IIC_Delay(); } }</code></pre> <p><span style="font-size:18px;color:#458FC3">(8)I2C接收一个字节</span></p> <p>与发送一字节类似,可以对照着来看,这里就不再啰嗦了。</p> <pre><code class="language-c">uint8_t IIC_Read_Byte(uint8_t ack) { uint8_t byte = 0; SDA_IN(); for (uint8_t i = 0; i &lt; 8; i++) { SCL_LOW(); IIC_Delay(); SCL_HIGH(); byte &lt;&lt;= 1; if (READ_SDA()) { byte |= 0x01; } IIC_Delay(); } return byte; }</code></pre> <h3>3.2.5 实现与温湿度传感器的I2C通信</h3> <p>这一块主要包含<code>softiic.c</code>中的3个函数,以及一个在<code>main.c</code>中调用的函数块。</p> <p>SHT40的地址和发送的命令在softiic.h中先进行了宏定义:</p> <pre><code class="language-c">#define SHT40_ADDRESS 0x44 // SHT40的I2C地址 #define SHT40_COMMAND_MEASURE_HIGH_PRECISION 0xFD //要发送的命令 0XFD</code></pre> <p><span style="font-size:18px;color:#458FC3">(1)<code>SHT40_Start_Measurement</code>开始测量温湿度函数</span></p> <p>其实是把下面的写入命令函数加了延时10ms,让SHT40有时间获取到温湿度值。可以合并进写函数(2),但个人认为那样不直观。</p> <pre><code class="language-c">void SHT40_Start_Measurement(void) { Soft_IIC_Write_Command(SHT40_ADDRESS, SHT40_COMMAND_MEASURE_HIGH_PRECISION); HAL_Delay(10); // 根据数据手册说明,延时10ms }</code></pre> <p><span style="font-size:18px;color:#458FC3">(2)<code>Soft_IIC_Write_Command</code> 发送命令函数</span></p> <p>设备地址是0x44,转化为7位二进制为 <code>1000100</code>,由于这里是写入,读写标志位为0(写),因此发送的这个字节为 <code>10001000</code>。</p> <p>“命令”为0xFD,这可以在数据手册中查到,对应的返回值是6字节数据,第1、2字节是温度数据,第3字节是CRC校验和;第4、5是湿度数据,第6位是校验和。这些数据都是高精度的。</p> <p><img src="//image.lceda.cn/oshwhub/1d8da8d57d894da58d7e74117fc8cef3.png" alt="image.png"></p> <p>根据原理部分讲的,在发送完地址后就要发送“寄存器地址”,而这里有个小坑,我最开始没绕明白。通常在网上搜到的I2C代码其写入函数参数有:设备地址、寄存器地址、写入的数据,而SHT40这的“命令”其实就是寄存器地址,只是这个寄存器是只读的。当SHT40接收到寄存器地址后,它就会去读取温湿度数据,并存在它的寄存器里,等待主机的读取命令。这里不需要给SHT40写入数据,也就是发送完寄存器地址后,等待应答然后关闭I2C传输,再等待0.01s,开始读取即可。</p> <pre><code class="language-c">void Soft_IIC_Write_Command(uint8_t deviceAddr, uint8_t command) { IIC_Start(); IIC_Send_Byte(deviceAddr &lt;&lt; 1); // 发送设备地址和写位 IIC_Wait_Ack(); IIC_Send_Byte(command); // 发送命令 IIC_Wait_Ack(); IIC_Stop(); }</code></pre> <p><span style="font-size:18px;color:#458FC3">(3)<code>SHT40_Read_Measurement</code>读取温湿度数据函数</span></p> <p>这里给SHT40发送读位,即读写标志位变为1,发送的字节为<code>10001001</code>。</p> <p>然后等SHT40回应,并释放SDA线,让SHT40发送它刚刚读到的温湿度数据,并存到data数组中。</p> <pre><code class="language-c">void SHT40_Read_Measurement(uint8_t* data, uint8_t length) { IIC_Start(); IIC_Send_Byte((SHT40_ADDRESS &lt;&lt; 1) | 0x01); // 发送设备地址和读位 IIC_Wait_Ack(); for (uint8_t i = 0; i &lt; length-1; i++) { *data = IIC_Read_Byte(i &lt; (length - 1)); // 读取数据并发送应答信号 data++; IIC_Ack(); } *data = IIC_Read_Byte(0); IIC_NAck(); IIC_Stop(); }</code></pre> <p><span style="font-size:18px;color:#458FC3">(4)main.c中获取温湿度数据的实现</span></p> <p>调用刚刚写好的函数<code>SHT40_Start_Measurement</code>和<code>SHT40_Read_Measurement</code>,这里的readData数组要提前初始化好,长度为6。</p> <p>然后根据数据手册里的伪代码</p> <p><img src="//image.lceda.cn/oshwhub/35d8d66917664fd584f6b757229a0372.png" alt="image.png"></p> <p>把获取到的数值换算成能读得懂的数,最后放大温湿度,以便数码管显示。</p> <pre><code class="language-c">/* SHT40采集温湿度*/ SHT40_Start_Measurement(); SHT40_Read_Measurement((uint8_t*)readData,6); Temperature = (1.0 * 175 * (readData[0] * 256 + readData[1])) / 65535.0 - 45; Humidity = (1.0 * 125 * (readData[3] * 256 + readData[4])) / 65535.0 - 6.0; device_paramter.Temp = Temperature * 10; //放大温湿度 device_paramter.Humi = Humidity * 10;</code></pre> <p>接下来,编译、烧录、插电开机~用示波器捕捉并解码收发数据过程</p> <p><img src="//image.lceda.cn/oshwhub/1411c9ee0e3e4fbd9823faab2e452744.png" alt="image.png"></p> <p>当你捕捉到这些跳动的高低电平那一刻,成就感瞬间爆棚啊~这就是电子的魅力~</p> <h2>3.3 优化程序降低功耗的方法</h2> <p>STM32G0系列有四种休眠模式;</p> <p>● 低功耗运行模式(降低CPU频率,系统仍在运行)</p> <p>● 睡眠模式(系统进入睡眠,任意中断/事件唤醒)</p> <p>● 停止模式(系统进入停止,支持任意外部中断和RTC闹钟唤醒)</p> <p>● 待机模式(系统进入待机,支持RTC闹钟唤醒,WKUP、NRST引脚唤醒以及IWDG复位唤醒,打开了LSI和LSE)</p> <p>如果按照官方文档的代码,让温湿度计进入<strong>睡眠模式</strong>,经测量<span style="font-size:17px;color:red">仍有2.6mA的电流。</span></p> <p><img src="//image.lceda.cn/oshwhub/584e9a08aaad4a4ea812615a6e1abe23.png" alt="image.png"></p> <p><img src="//image.lceda.cn/oshwhub/c1d056d5960b41a799ac41662a450cfb.jpg" alt="IMG_20240717_220545.jpg"></p> <p>而如果设置成<strong>停止模式</strong>,仍然可以用中断唤醒,但此时电流<span style="font-size:17px;color:red">降低至0.21mA !!</span></p> <p><img src="//image.lceda.cn/oshwhub/36b835d4fbd7487ab98e0011cddae313.png" alt="image.png"></p> <p><img src="//image.lceda.cn/oshwhub/0cb87bd082144ab6b355da3e8e3d7cdc.jpg" alt="IMG_20240717_220141.jpg"></p> <h2>3.4 程序烧录步骤</h2> <p>(1)从附件中下载SHT40_Project.zip,并解压,进入MDK-ARM文件夹,用Keil打开SHT40_Project.uvprojx</p> <p>(2)将ST-LINK针对针地与温湿度传感器电路板上的调试口相连接</p> <p>(3)点击编译(Build),点击下载(Download)</p> <p><img src="//image.lceda.cn/oshwhub/cb4d5ff130154ad1b3ff82105bc7c007.png" alt="image.png"></p> <p>(4)断开ST-LINK,在电路板背面放入18650锂电池,注意一定不要接反!(有弹簧一侧为负极)</p> <p>(5)正常来说,上电后数码管不亮,按一下按钮,两个数码管同时显示温湿度5秒,然后左侧数码管显示电池电压4秒,然后进入休眠。</p> <h1>4 故障排除</h1> <p><span style="font-size:19px;color:red">故障1:上电后只有第一次温湿度读数,而后续读数错误。</span></p> <p><span style="font-size:19px;color:orange">故障描述:</span>插电,发现只显示读出第一次的温湿度数值,之后进入while循环的数值就读不到了,数码管显示0.0 9.0。</p> <p><span style="font-size:19px;color:orange">解决过程:</span>经过一番摸索,在我用示波器表笔接SCL,准备再次查看波形时,发现无波形变化,遂确认是硬件问题而非软件BUG。于是我用万用表电阻档再次测量3.3V与SCL、SDA之间的电阻,发现它们均不为4.7KΩ(上拉至3.3V的电阻值),于是拆下来两个电阻挨个测量,最后发现SCL的上拉的电阻损坏</p> <p><span style="font-size:19px;color:orange">故障原因:</span><span style="font-size:16px;color:#1E90FF">上拉电阻损坏。</span></p> <p><span style="font-size:19px;color:red">故障2:数码管显示0.0和9.0</span></p> <p><span style="font-size:19px;color:orange">故障描述:</span>上电后,数码管读不到正确的温湿度值,不同于故障1还有一次可读的数值。</p> <p><span style="font-size:19px;color:orange">解决过程:</span>显示0.0和9.0是由于初始化的数据接收数组内元素都为0,而通信读取温湿度过程中数组内数值并未改变,代入温湿度的计算公式得到了固定值0.0和9.0。可用测量通断挡位,一支表笔接传感器模块插口,一支表笔接STM32对应引脚,分别测试SCL、SDA、VCC、GND判断是否接触良好。若接触良好,则有可能传感器损坏。</p> <p><span style="font-size:19px;color:orange">故障原因:</span><span style="font-size:16px;color:#1E90FF">接触不良或温湿度传感器损坏</span></p> <p><span style="font-size:19px;color:red">故障3:想再次烧录代码,Keil提示invalid rom table</span></p> <p><span style="font-size:19px;color:orange">故障描述:</span>已经烧录好我在附件里的源代码,想改动一点自己的东西,再烧录发现Keil报错。</p> <p><span style="font-size:19px;color:orange">解决过程:</span>由于有让MCU进入休眠模式的代码,在上电后它会立即进入休眠模式,以保持低功耗运行。此时可以先插好ST-LINK,在Keil程序编译好,然后按下唤醒按钮,在数码管仍在显示数值的过程中,手快速地点击Keil的下载按钮即可。 . <span style="font-size:19px;color:orange">故障原因:</span><span style="font-size:16px;color:#1E90FF">MCU进入休眠模式</span></p> <h1>5 项目属性</h1> <p>首次公开,在官方原理图基础上增加锂电池充电管理电路、DC-DC升压以及LDO降压电路,以及优化PCB布局,编写软件I2C等代码。未在别的比赛中获奖。</p> <h1>6 开源协议</h1> <p>GPL3.0</p> <h1>7 大赛LOGO验证</h1> <p>温湿度计电路板整体外观展示 <img src="//image.lceda.cn/oshwhub/1477be0bda8f4354829d7978ba8bd863.jpg" alt="IMG_20240718_214156.jpg"></p> <p>显示温度、湿度状态展示 <img src="//image.lceda.cn/oshwhub/70cd8d5a63364b05b3982e83c3cbdcce.jpg" alt="IMG_20240718_214309.jpg"></p> <p>显示电池电压 <img src="//image.lceda.cn/oshwhub/b5c607f509d9436e8aa79610a2e4f503.jpg" alt="IMG_20240718_214300.jpg"></p> <h1>8 演示视频</h1> <p>演示视频可在附件查看,也同步上传至B站:</p> <p><a href="https://www.bilibili.com/video/BV1C4421Z7rL/" target="_blank">https://www.bilibili.com/video/BV1C4421Z7rL/</a></p> <h1>写在后面</h1> <p>第二次参加训练营啦,这次借着温湿度计训练营一同参加了第九届立创电赛。在设计硬件、调试软件的过程中真的能学到很多东西!查资料非常重要,不论是国内国外的资料,在查的过程中也能逐渐理清知识脉络。</p> <p>在这里要感谢<a href="https://sensirion.com/cn/" target="_blank">@盛思锐</a>,温湿度传感器很好用,数据手册内容内容非常全面。</p> <h1>参考</h1> <ol> <li><a href="https://blog.csdn.net/qq_38575895/article/details/127641344" target="_blank">https://blog.csdn.net/qq_38575895/article/details/127641344</a></li> <li><a href="https://blog.csdn.net/qlexcel/article/details/117159467" target="_blank">https://blog.csdn.net/qlexcel/article/details/117159467</a></li> <li><a href="https://www.bilibili.com/video/BV1dg4y1H773/?spm_id_from=333.337.search-card.all.click&amp;vd_source=b0a6c01daa3f29bd494dc070c1c1bf14" target="_blank">https://www.bilibili.com/video/BV1dg4y1H773/?spm_id_from=333.337.search-card.all.click&amp;vd_source=b0a6c01daa3f29bd494dc070c1c1bf14</a></li> <li><a href="https://www.yuque.com/wldz/jlceda/ul1wcz7n5dgt6s60" target="_blank">https://www.yuque.com/wldz/jlceda/ul1wcz7n5dgt6s60</a></li> </ol>

文档

BOM

暂无

附件

附件名 下载
演示视频.mp4
SHT40_Project.zip

评论(18)

  • 表情
    emoji
    小嘉工作篇
    小嘉日常篇
  • 图片
成功
工程所有者当前已关闭评论
dianzidiylalala 回复
<p>请问如果我按照官方示例电路打板焊接,还可以烧录大佬你给的程序吗,我按官方电路打板焊接完,烧录你给的程序发现显示0.0,9.0,测量4个引脚均为通,不会真的是sht40坏了吧,要如何判断sht40的好坏呢</p>
liubc100 回复
<p>我也是SCL SDA画反了 [emojis:憨笑]</p>
vrxiaojie 回复
<p>按官方的来的话,你需要改一下我程序里的SDA和SCL对应的引脚。在softiic.h的 第8、9行</p>
vrxiaojie 回复
<p>哈哈哈,我是其他都焊完了,就差插上SHT40的时候,发现弄反的,当时不知道有软件I2C这个东西,整个人是崩溃的,飞了根线出来测试了功能,然后到群里找大佬询问,才得知有软件I2C的。[emojis:愉快]</p>
liubc100 回复
<p>我就飞了线,程序不会,刚开始接触</p>
dianzidiylalala 回复
<p>好的谢谢大佬,我说呢为什么显示不对[emojis:呲牙]</p>
albertkai 回复
<p>写的很详细,mark了,我也来复刻下</p>
好好公子呢 回复
<p>可以,很详细</p>
moby008 回复
<p>厉害 都开源发布了!赞</p>
pyfei 回复
<p>SN74HC595_Send_Data(SN_LED1,(sgh_value[value]|0x80));<br/>value|0x80,在这里起什么作用呢</p>
vrxiaojie 回复
<p>sgh_value数组里,sgh_value[10]的值是0x80,是用于显示数码管上dp段,也就是小数点的。<br/>用0x80和sgh_value数组里的某一个值做按位或运算,因为0x80的二进制对应1000 0000,和任意一个数做或运算,那个数的低7位的值保持不变,最高位的值变为1。<br/>因此如果显示的是"0.",要在数码管上显示0,也就是要开启abcdef段,即二进制的0011 1111,十六进制的0x3F,0x3F与0x80做或运算,得到1011 1111,最高位变为了1,也就让dp段亮。达到了显示小数点的目的</p>
vrxiaojie 回复
<img title="点击查看大图" src="//image.lceda.cn/oshwhub/2e8f2b7c71d549ddae787285ff2a25f2.jpg" alt="">
vrxiaojie 回复
<p>[emojis:点赞]大家走过路过的麻烦点个赞呀~</p>
pyfei 回复
<p>[emojis:强]</p>
xiao_a_bin 回复
<p>太强了,大佬!</p>
weisy 回复
<p>我靠,大佬写的和做的非常的牛逼</p>
maker114 回复
<p>电源设计部分各个元件的连接可以用小面积的铺铜或者填充区域实现,这样效果会更好,一般的电源芯片的说明书里有layout参考图可以看看</p>
vrxiaojie 回复
<p>学习到了,感谢!</p>
goToTop
svg-battery svg-battery-wifi svg-books svg-more svg-paste svg-pencil svg-plant svg-ruler svg-share svg-user svg-logo-cn svg-double-arrow