【嘉楠科技 CanMV K230测评】基础外设学习一——Pin、Timer、RTC、ADC
本帖最后由 王嘉辉 于 2024-10-2 23:16 编辑<p><strong>点灯</strong></p>
<p>“点亮LED灯”即点灯实验是我们学习嵌入式软件开发过程中必须要经历的一个demo,所以在学习这个开发板的时候,我们也从这个例程开始这块开发板的学习。</p>
<p>LED灯其本质上就是一个发光二极管,在这个二极管上加上一个合适的电压(注意二极管的单向导电性,不要接反,不过在开发板上不需要注意这些),下面是这个开发板的原理图上的LED灯的电路原理,可以看到二极管的负极接在了GND上,正极串联一个电阻接在了芯片的IO引脚上。串联电阻的目的是为了限制电流,可以延长LED灯的使用寿命,同时也可以控制LED灯的亮度。</p>
<div style="text-align: center;"></div>
<div style="text-align: center;">
<p style="text-align: left;">根据提供的例程和文档可以发现点亮一个LED灯需要使用的内部外设有FPIOA和Pin两个部分。</p>
<p style="text-align: left;">K230的功能很多,所以大部分的IO都会复用多种功能,所以K230内部引入了<strong>FPIOA</strong>的概念,即Field Programmable Input and Output Array 现场可编程IO阵列,这个概念其实和IOMUX类似。从而实现更加灵活的IO功能的选择和使用。下面是API手册中关于FPIOA的相关介绍。</p>
<p style="text-align: left;"><a href="https://developer.canaan-creative.com/k230_canmv/main/zh/api/machine/K230_CanMV_FPIOA%E6%A8%A1%E5%9D%97API%E6%89%8B%E5%86%8C.html#" target="_blank">2.8 FPIOA 模块API手册 — K230 CanMV</a></p>
<p style="text-align: left;">在API手册中可以看到FPIOA类位于machine模块下,因此我们在使用这个类的时候要从machine模块中进行引用。</p>
<p style="text-align: left;">首先使用fpioa = FPIOA()构造一个函数。</p>
<p style="text-align: left;">下面简单介绍一下可能用到的函数,更详细的内容可以参考上面提供的API手册进行查找。</p>
<p style="text-align: left;">首先第一个是最最最最常用的函数,就是给一个引脚指定对应的功能,以及给这个功能配置一些基本的属性,比如上拉下拉、输入输出使能或者驱动能力等等。</p>
<p style="text-align: left;">FPIOA.set_function(pin, func, ie=-1, oe=-1, pu=-1, pd=-1, st=-1, sl=-1, ds=-1)</p>
<p style="text-align: left;">其中pin为引脚号,可选的范围0-63。func为功能号,可以被配置为普通的IO或者其可被配置的其他功能如串口等等。ie、oe、pu、pd分别对应着输入使能、输出使能、配置上拉和配置下拉。st和sl分别是st使能和sl使能。ds为配置驱动能力。(在后面的pin模块的使用中,也可以配置普通IO以及普通IO所能配置的输入输出模式、上下拉以及驱动能力。)</p>
<p style="text-align: left;">除此之外,FPIOA的部分还提供了一些可以用于查询的函数(通过引脚查询该引脚所配置的功能和通过功能查询配置该功能的引脚)。</p>
<p style="text-align: left;">PWM.get_pin_num(func)</p>
<p style="text-align: left;">PWM.get_pin_func(pin)</p>
<p style="text-align: left;">当然也可以使用PWM.help()函数进行引脚或功能的所有查询。</p>
</div>
<p>下面简单介绍一下<strong>Pin</strong>相关的函数。<a href="https://developer.canaan-creative.com/k230_canmv/main/zh/api/machine/K230_CanMV_Pin%E6%A8%A1%E5%9D%97API%E6%89%8B%E5%86%8C.html" target="_blank">2.6 Pin 模块API手册 — K230 CanMV</a></p>
<p>Pin类也位于machine模块下,因此我们在使用这个类的时候要从machine模块中进行引用。</p>
<p>首先使用pin = Pin(index, mode, pull=Pin.PULL_NONE, drive=7)构造一个函数。其中的参数分别是引脚号、输入或输出模式、上拉或下拉选择、驱动能力选择。</p>
<p>当然也可以使用Pin.init(mode, pull=Pin.PULL_NONE, drive=7)进行重新的配置。其中参数的介绍参考上面的介绍。</p>
<p>也可以使用Pin.mode()、Pin.pull()、Pin.drive()分别进行输入输出模式、上下拉和驱动能力的单独的配置。</p>
<p>Pin.value()函数用于配置引脚在输出模式下,输出高电平或低电平。也可以使用Pin.on()、Pin.off()、Pin.high()、Pin.low()进行电平高低的配置。如果这个函数在引脚的输入模式下,同时不传入参数,就可以获取引脚的电平。</p>
<p>现在开始进行点灯的实验。</p>
<p>在IDE中点击文件->新建文件,IDE中会打开一个新的文件,然后先保存一下(只有改动后才可以进行保存)。</p>
<div style="text-align: center;"></div>
<p>先把提供的代码删掉,进行点灯的代码的编写。</p>
<pre>
<code class="language-python">from machine import Pin
from machine import FPIOA
import time
#将GPIO52配置为普通GPIO
fpioa = FPIOA()
fpioa.set_function(52,FPIOA.GPIO52)
LED=Pin(52,Pin.OUT)
LED.value(1)
time.sleep(3) # 睡眠3秒
LED.value(0)
time.sleep(3) # 睡眠3秒
LED.value(1)</code></pre>
<p>首先在代码的前三行引入了三个模块,分别是来自machine模块下的FPIOA和Pin类以及一个time模块,用于产生一定的延时。</p>
<p>然后构建一个FPIOA的函数,并将引脚52配置为普通IO模式(根据上面的原理图可以看到LED被连接在52号引脚上)。</p>
<p>然后构建一个LED的函数,并将引脚52配置为输出模式。</p>
<p>然后使用LED.value(1)和LED.value(0)用于控制引脚输出高电平和低电平,从而实现LED的点亮和熄灭。</p>
<p>但是过于快速的切换,我们的肉眼无法观察到,所以要在切换的时候加入一定时间的延时,即time.sleep(3)函数。</p>
<p>连接开发板,并开始运行,观察开发板的LED。</p>
<p>44b4f3e52e4b1e5ac69067bb5d726979</p>
<p><strong>按键</strong></p>
<p>按键被按下时相当于一段导线,松开时相当于是断路。这样按键被连接在GPIO上。另一端连接在GND上,同时在GPIO一侧连接一个上拉电阻。当按键按下时,GPIO被连接到GND上,读到的电平就是低电平,当按键松开时,GPIO的电平状态被上拉电阻稳定到高电平,读到的电平就是高电平。实现了通过GPIO检测按键是否按下。</p>
<div style="text-align: center;"></div>
<p>按键的软件开发和LED的软件开发所使用的模块是一样的,都是使用FPIOA和Pin。</p>
<pre>
<code class="language-python">from machine import Pin
from machine import FPIOA
import time
fpioa = FPIOA()
fpioa.set_function(52,FPIOA.GPIO52)
fpioa.set_function(21,FPIOA.GPIO21)
LED=Pin(52,Pin.OUT)
KEY=Pin(21,Pin.IN,Pin.PULL_UP)
state=0
while True:
if KEY.value()==0:
time.sleep_ms(10)
if KEY.value()==0:
state=not state
LED.value(state)
while not KEY.value():
pass</code></pre>
<p>首先在代码的前三行引入了三个模块,分别是来自machine模块下的FPIOA和Pin类以及一个time模块,用于产生一定的延时。</p>
<p>然后构建一个FPIOA的函数,并将引脚52配置为普通IO模式(根据上面的原理图可以看到LED被连接在52号引脚上),同时将引脚21配置为普通IO模式(根据上面的原理图可以看到KEY被连接在21号引脚上)。</p>
<p>然后构建一个LED的函数,并将引脚52配置为输出模式。同时构建一个KEY的函数,并将引脚21配置为输入模式,同时配置内部上拉。</p>
<p>定义一个新的变量state用来表示LED的状态。</p>
<p>然后写一个循环while True: 类似于C语言中的while(1)。</p>
<p>通过if语句判断按键是否按下,然后延时10ms用于按键消抖,再次判断按键是否按下,若按下,则将state进行翻转。再使用LED赋值函数将目前的state值赋给LED从而可以改变LED的状态,实现按键控制LED亮灭切换。</p>
<p>最后加上一个pass函数,用于避免按键连按。</p>
<p>下载代码的效果如下面的视频所示。每次按键按下都会切换LED的亮灭状态。</p>
<p>2b66891655d1075d3120f1d133a7f0c2<br />
<strong>Timer</strong></p>
<p>定时器的作用是用来产生一个精准的计时,当到达我们设定的时间后可以提醒我们去做一些事情。</p>
<p>Timer类位于machine模块下,因此我们在使用这个类的时候要从machine模块中进行引用。</p>
<p>Timer的函数较少,仅有构造函数、初始化以及释放函数三个。</p>
<p>使用timer = Timer(index, mode=Timer.PERIODIC, freq=-1, period=-1, callback=None, arg=None)进行Timer函数的构造,其中的参数分别对应着Timer通道(可选的范围为-1~5,-1为软件定时器,但是在01studio的文档中解释的是目前仅有软件定时器可以使用);mode用于选择Timer的工作模式,是循环进行定时还是定时一次后就结束;freq用于设定定时器定时的频率;period用于设定定时器定时的周期(当freq和period同时被给出时,会优先使用freq,此时的period会被屏蔽);callback用于设置超时回调函数;arg用于设置超时回调函数的参数。</p>
<p>使用Timer.init(mode=Timer.PERIODIC, freq=-1, period=-1, callback=None, arg=None)可以进行Timer的初始化,这个函数中的参数可以参考构造函数的参数。</p>
<p>Timer.deinit()函数用于释放Timer。</p>
<p>下面我们使用Timer进行点灯的实验。 点灯的频率为5Hz,即200ms为一个周期。</p>
<pre>
<code class="language-python">from machine import Timer,Pin
import time
led = Pin(52,Pin.OUT)
state = 0
def fun(i):
global state
state = not state
led.value(state)
#使用软件定时器,编号-1
tim = Timer(-1)
tim.init(mode=Timer.PERIODIC, period=100, callback=fun) #周期为100ms
while True:
time.sleep(0.01) #避免CPU满跑</code></pre>
<p>首先引入Timer和Pin。</p>
<p>然后构建一个LED的函数,并将引脚52配置为输出模式。创建一个全局变量state,用于表示LED的状态。</p>
<p>定义回调函数,在回调函数中首先先声明一下全局变量state,然后对state进行取反,然后将state的数值赋给LED。</p>
<p>初始化Timer使用软件定时器,传入参数为-1。并配置模式为循环模式,周期为100ms(此时的周期为LED闪烁周期的一半,是因为LED闪烁一个周期包含点亮和熄灭两个过程,因此应该是Timer的两倍),回调函数为fun,即刚才我们定义的回调函数。</p>
<p>然后在死循环中运行一个sleep的延时,避免CPU满跑。</p>
<p>效果演示如下。</p>
<p>6da730c27c4298ad62a3290a979e18e6<br />
<strong>RTC</strong></p>
<p>RTC全称是Real Time Clock,可以用来进行精准的时间计时,我们现在常用的万年历或数字时钟常常就是使用类似的芯片DS1302等等实现计时的功能。K230内置一个RTC,但是这块板子上没有额外的电池,所以要想让RTC一直工作的话,需要板子一直进行上电。</p>
<p>RTC类位于machine模块下,因此我们在使用这个类的时候要从machine模块中进行引用。</p>
<p>RTC的主要功能就是获取实时的时间,当然也可以使用函数进行重新设定时间。</p>
<p>rtc.init(year,mon,day,hour,min,sec,microsec)函数用于时间的重新设定,其中的参数依次是年、月、日、小时、分钟、秒、微秒。</p>
<p>rtc.datetime()函数用于时间的获取。这个的返回量是一个数组,所以可以配置打印不同的位,从而单独的获取年、月、日等等。</p>
<p>像rtc.datetime()、rtc.datetime()这样可以获取年和月的数值。然后可以使用print进行打印或者赋值给其他的变量进行其他的处理。</p>
<p>下面进行RTC的初始化和时间打印。</p>
<pre>
<code class="language-python">from machine import RTC
import time
rtc = RTC()
if rtc.datetime() != 10:
rtc.datetime((2024, 10, 1, 0, 0, 0, 0, 0))
while True:
print(rtc.datetime())
time.sleep(1)</code></pre>
<p>首先引入RTC。</p>
<p>然后构建一个RTC的函数。</p>
<p>写一个判断语句,判断RTC的第二位即rtc.datetime()不等于10的时候,给RTC初始化为2024年10月1日0点0分0秒。</p>
<p>然后在循环中,打印当前的RTC时间。time的作用是每隔一秒打印一次,防止高速的刷屏。</p>
<p>下面是RTC的演示视频。</p>
<p>80640f16b8b645883a3bf913d16c2aed<br />
<strong>ADC</strong></p>
<p>物理世界中的大部分的信号都是模拟量,但是随着单片机的发展,越来越多需要数字信号的处理,因此将模拟信号转换成为数字信号就显得尤为重要。</p>
<p>K230内置了一个ADC,其具有6个通道(该开发板引出了四个ADC通道,分别是0-4,其中0和1的量程为0-3.6V,2和3的量程为0-1.8V),采样分辨率为12bit,采样的速率可以达到1MHz。</p>
<p>ADC类位于machine模块下,因此我们在使用这个类的时候要从machine模块中进行引用。</p>
<p>构造函数的方式是adc = ADC(channel),其中的参数channel为ADC通道的选择,可以选择的范围为0-5。</p>
<p>获取ADC数值的方式有两种,一种是直接读出寄存器的数值,不进行单位转换,对应的数据范围是0-4095。另一种是转换为电压值进行输出,返回值的范围是0-1800000。这两个函数分别是ADC.read_u16()和ADC.read_uv()。</p>
<p>下面进行代码的演示。</p>
<pre>
<code class="language-python">from machine import ADC
import time
adc = ADC(0)
while True:
print(adc.read_u16())
print('%.2f'%(adc.read_uv()/1000000*2), "V")
time.sleep(1)
</code></pre>
<p>首先引入ADC。</p>
<p>构建一个ADC的函数,并使用0通道,对应的引脚为32,量程为0-3.6V</p>
<p>在循环中持续打印两个数据。首先是直接打印ADC通道的采样值,得到的数据的范围是0-4095。</p>
<p>然后打印实际的电压值,函数的电压的单位是uV,因此首先要进行单位的转换,除上1000000将单位换成V。通道0的检测范围最大可以到3.6V,但是数值的显示最大仅有1.8V,因此还要进行乘2,从而实现准确数值的显示。</p>
<p>进行1s的延时。演示的效果如下所示。</p>
<p>0d8544e055bea7f22fb60a15e9f34ae0<br />
</p>
<p>通过对K230的基本外设的熟悉,基本上掌握了Pin、Timer、ADC、RTC的使用,后面会进行PWM、UART等其他外设的介绍,以及图像识别和机器学习相关的分享。</p>
页:
[1]