【K210】K210学习笔记四——定时器的使用

【K210】K210学习笔记四——定时器的使用

  • 前言
  • 定时器
  • 定时器配置
  • 定时器使用方法
  • 测试
  • 小车简单巡线
  • PWM配置
  • 控制小车巡线的方法
  • 测试
  • 完整源码
  • 前言

    本人大四学生,电赛生涯已经走到尽头,一路上踩过不少坑,但运气也不错拿了两年省一,思来想去,决定开始写博客,将电赛经验分享一二,能力有限,高手轻喷。
    往期的博客讲述了 K210 的感光元件模块 sensor 的配置,机器视觉模块 image 中部分函数的使用(目前是用 find_blobs 函数实现一些寻找不同颜色的目标点,寻找不同颜色的线,后面会更新更多 image 模块中的函数使用方法),以及按键、LCD、LED的使用。

    sensor 的学习笔记传送门
    【K210】K210学习笔记一——sensor
    image 的学习笔记传送门
    【K210】K210学习笔记二——image
    按键、LCD、LED的使用 的学习笔记传送门
    【K210】K210学习笔记三——按键、LCD、LED的使用

    本文着重于 K210 的定时器配置及使用方法,结合往期代码,做一个简单的巡线小车。无需将目标信息传输到STM32,仅用K210上的定时器输出PWM即可完成。主要讲一下是怎么样使用定时器的,另外跟随目标的逻辑是怎么样的。若要将识别信息传输至STM32,可看本人往期博客,了解串口发送与接收,传送门如下。

    串口通信 传送门
    【串口通信】K210与STM32串口通信、K210与OpenMV串口通信

    定时器

    定时器配置

    配置定时器需要导入一些模块,这里连同PWM的模块也一起导入,后面的配置就不需要导入了。

    from machine import Timer, PWM                              # 从 machine 模块中导入 定时器模块 Timer 脉宽调制模块 PWM
    
    

    与往期一样的,定义一个类来保存定时器的一些属性,我个人喜欢定义类来保存,因为这样可以提高效率,提高代码可读性,变量名称看起来不会很怪,可以一下子知道这个变量是什么。

    #__________________________________________________________________
    # 定时器的使用
    # 定义定时器属性类
    class timer_property():
        cnt     = 0                                             # 定时器计数值
        cnt_max = 0                                             # 定时器计数值上限
        period  = 0                                             # 定时器周期
        freq    = 0                                             # 定时器频率
        
    

    然后就是实例化类,我是打算将定时器0配置为一个计数器,每次触发定时器中断,这个计数器的值加1,加到上限值,就将计数器的值置0,这样我们就可以在 while 循环中,判断这个计数器的值是否等于0,从而控制一些不需要高频率执行的函数的执行周期(比如各种打印信息) ,这种方法会比之前博文中使用的计数器自增控制周期的方法 更准确。但会存在一定的误差的,这是因为是在 while 循环中做的判断,while 循环的周期是不一定的(周期就是fps 比如fps等于20 则代表一秒会有20个循环),如果图像识别的速度很慢(数据量大),那么 while 循环的周期就很长,可能一秒就只有几次。

    # 定时器0 配置_______________________________________________________
    # 定时器0 实例化类
    timer0 = timer_property()                                   # 实例化定时器属性类 timer_property() 为 timer0
    timer0.cnt_max = 9                                          # 设定 定时器0 的计数值上限为 9
    timer0.period = 100                                         # 设定 定时器0 的周期为 100
    
    

    我在回调函数中只是做一个计数器值自增的操作,你也可以将需要间隔一定时间做的函数放到回调函数中,但需要注意的是,这个函数千万不能占用太长的时间,否则得不偿失。这也就是为什么我只在回调函数中做一个简单的计数器值自增,将实际要控制执行周期的函数放到 while 循环中的原因。

    # 定时器0 定义回调函数
    def timer0_back(tim0):
        if timer0.cnt < timer0.cnt_max:                         # 若 定时器0 的计数值小于 定时器0 的计数值上限
            timer0.cnt = timer0.cnt + 1                         # 计数值自增
        else:
            timer0.cnt = 0                                      # 超出计数值上限 则计数值重置为0
            
    

    然后就是定时器的初始化,这里配置的是定时器0,通道0,模式为周期性(模式有一次性、周期性、PWM,一般都是周期性或PWM,一次性使用的很少),周期的单位为ms,周期为设定值 timer0.period这个变量在之前被设置为100,也就是说,定时器0的周期是100ms。

    # 定时器0 初始化
    tim0 = Timer(Timer.TIMER0,                                  # 定时器编号 定时器0
                Timer.CHANNEL0,                                 # 定时器通道 通道0
                mode = Timer.MODE_PERIODIC,                     # 定时器模式 周期性
                unit = Timer.UNIT_MS,                           # 定时器周期单位 ms
                period = timer0.period,                         # 定时器周期 timer0.period 若 unit 为 Timer.UNIT_MS 则周期为 timer0.period ms
                callback = timer0_back)                         # 定时器触发中断后执行的回调函数 timer0_back
                
    

    定时器使用方法

    使用方法就很简单了,在 while 循环中,做一个判断,当定时器0的计数器值被重置为0的时候,执行函数即可。我这里控制的是打印各参数信息的函数,因为我不需要这些参数发送的太快,发送太快我也看不清。你可以用它来控制你想控制的其他函数,比如可以用来控制串口发送,如果这里加一句串口发送函数的话,那么这个串口发送函数就是1秒发送一次。

        if timer0.cnt == 0:                                     # 如果 timer0.cnt 等于 0 此步骤的目的是控制打印周期 不要打印的太快
            print_sensor()                                      # 打印sensor参数
            print_blobs_property(black,"Black-")                # 打印黑色色块参数
            print_blobs_property(red,  "Red-  ")                # 打印红色色块参数
            
    

    测试

    将K210连接到MaixPy IDE,然后运行即可,在串行终端可以看到这些参数差不多间隔1s就会被打印一次。

    小车简单巡线

    PWM配置

    首先还是定义一个电机类,来保存电机的信息,推荐大家养成这个习惯,对大家有好处。

    # 定时器1 配置_______________________________________________________
    # 电机类定义
    class motor_property():
        motor1      = 0                                         # 电机1 占空比
        motor2      = 0                                         # 电机2 占空比
        motor3      = 0                                         # 电机3 占空比
        motor4      = 0                                         # 电机4 占空比
    
        motor1_pin  = 0                                         # 电机1 引脚
        motor2_pin  = 0                                         # 电机2 引脚
        motor3_pin  = 0                                         # 电机3 引脚
        motor4_pin  = 0                                         # 电机4 引脚
    
        control_x   = 0                                         # 被控坐标 x
        control_y   = 0                                         # 被控坐标 y
        
    

    然后是实例化电机类,这里我只写了电机1和电机2,如果你要做四驱小车,可以再写电机3和电机4。 然后PWM输出引脚(也就是电机所接的引脚)我是设置电机1的引脚为14,电机2的引脚为13。这个是K210开发板上红灯和绿灯的引脚,主要是当前放暑假在家,手头上没有小车,所以这里先用LED来观察一下效果,看看逻辑是否正确,回校后我会在小车上进行测试,并更新此博文

    # 实例化电机类
    motor = motor_property()                                    # 实例化电机类 motor_property() 为 motor
    motor.motor1 = 50                                           # 电机1的占空比 初始设置为 50%
    motor.motor2 = 50                                           # 电机2的占空比 初始设置为 50%
    motor.motor1_pin  = 14                                      # 电机1的引脚 14为红灯引脚 这里先用灯的亮灭观察效果
    motor.motor2_pin  = 13                                      # 电机2的引脚 13为绿灯引脚 这里先用灯的亮灭观察效果
    
    

    然后实例化一个定时器属性类,保存一下PWM占空比的频率信息。这里设置是1K,如果你需要改成其他数值也可以,比如10K。

    # 定时器1 实例化类
    timer1 = timer_property()                                   # 实例化定时器属性类 timer_property() 为 timer1
    timer1.freq = 1000                                          # 设定 定时器1 的频率为 1000
    
    

    然后就是PWM定时器的设置,这里我用的是定时器1,我这里只设置了两个通道,如果你要做四驱车,就复制一下这个代码,改个名字即可

    # 定时器1 通道0 初始化
    tim1_ch0 = Timer(Timer.TIMER1,                              # 定时器编号 定时器1
                     Timer.CHANNEL0,                            # 定时器通道 通道0
                     mode = Timer.MODE_PWM)                     # 定时器模式 PWM
    
    # 定时器1 通道1 初始化
    tim1_ch1 = Timer(Timer.TIMER1,                              # 定时器编号 定时器1
                     Timer.CHANNEL1,                            # 定时器通道 通道1
                     mode = Timer.MODE_PWM)                     # 定时器模式 PWM
                     
    

    然后是创建电机对象,需要注意的就是这里要跟上面的那些参数名字对应的上。

    # 创建对象 电机1 通道为 定时器1的通道0 频率为 定时器1的频率 占空比为 电机1的占空比 引脚为 电机1的引脚
    motor1 = PWM(tim1_ch0, freq = timer1.freq, duty = motor.motor1, pin = motor.motor1_pin)
    
    # 创建对象 电机2 通道为 定时器1的通道1 频率为 定时器1的频率 占空比为 电机2的占空比 引脚为 电机2的引脚
    motor2 = PWM(tim1_ch1, freq = timer1.freq, duty = motor.motor2, pin = motor.motor2_pin)
    
    

    控制小车巡线的方法

    控制小车巡线就太简单了,将摄像头识别到的坐标,和要被控制到的坐标值传入即可,转换成0到50之间的值,然后控制电机转速即可。这不单单是巡线可以这样做,你也可以控制小车跟随其他目标,比如你可以拿一个红色的纸,放到摄像头的左边,小车就会往左边开。

    # 定义电机占空比控制函数
    def motor_control(motor, x):
        val = 0
        if x < motor.control_x:                                 # 若 当前坐标 小于 被控坐标x 即当前状态小车在目标的 左边
            val = (motor.control_x - x) * 0.3125                # 获取坐标差值 并转换为 0~50 之间的值
            motor.motor1 = 50 - val                             # 减小 电机1 占空比 电机1为左电机 使小车右转
            motor.motor2 = 50 + val                             # 增大 电机2 占空比 电机2为右电机 使小车右转
    
        elif x > motor.control_x:                               # 若 当前坐标 大于 被控坐标x 即当前状态小车在目标的 右边
            val = (x - motor.control_x) * 0.3125                # 获取坐标差值 并转换为 0~50 之间的值
            motor.motor1 = 50 + val                             # 增大 电机1 占空比 电机1为左电机 使小车左转
            motor.motor2 = 50 - val                             # 减小 电机2 占空比 电机2为右电机 使小车左转
    
        motor.motor1 = int(motor.motor1)                        # 将 电机1占空比 转换为 整数
        motor.motor2 = int(motor.motor2)                        # 将 电机1占空比 转换为 整数
        
    

    具体实现是这样实现的,首先会拍摄一张图片,然后在这张图片中找红色色块,获取红色色块的坐标值,然后设定将该坐标值控制到160。通过电机占空比控制函数获取占空比,将占空比重新装载即可,逻辑就是。
    比如我识别到红色色块的坐标为20,而我要控制它走到160的位置,20说明小车当前在该红色目标的右边,那么我就要控制它左转,同样的,如果识别到的坐标是300,那就说明小车在目标左边,右转即可,如此一来便可完成巡线或者是目标跟踪。

    #__________________________________________________________________
    # 主函数
    while(True):
    
        clock.tick()                                            # 跟踪运行时间
    
        img = sensor.snapshot()                                 # 拍摄一张照片
    
        #opv_find_blobs(black,1)                                # 找黑色色块 led标志为1 表示黑色
        opv_find_blobs(red,2)                                   # 找红色色块 led标志为2 表示红色
    
        point_control(key)                                      # 按键控制下的目标点获取函数
    
        lcd.display(img)                                        # LCD 显示图像
        lcd_key()                                               # LCD 显示按键信息及目标点信息
    
        #led_control(red.led_flag)                              # LED 标记色块识别情况
    
        motor.control_x = 160                                   # 控制目标处于 x轴中心点 160
        motor_control(motor,red.cx)                             # 电机占空比控制函数获取电机控制占空比
    
        motor1.duty(motor.motor1)                               # 将获取到的电机1占空比 装载
        motor2.duty(motor.motor2)                               # 将获取到的电机2占空比 装载
    
        if timer0.cnt == 0:                                     # 如果 timer0.cnt 等于 0 此步骤的目的是控制打印周期 不要打印的太快
            print_sensor()                                      # 打印sensor参数
            print_blobs_property(black,"Black-")                # 打印黑色色块参数
            print_blobs_property(red,  "Red-  ")                # 打印红色色块参数
    
    #__________________________________________________________________
    
    

    测试

    这部分先用LED灯的情况来模拟,后面会装到小车上进行测试,后续会更新此博文。
    可以看到位置差不多在160附近的时候,motor1和motor2的占空比为50左右,表示直走。

    此时两个灯亮的程度基本相当,呈现黄色,说明逻辑正确。

    可以看到位置差不多在280附近的时候,motor1的占空比为87,motor2的占空比为12,表示右转。

    此时绿灯较亮,绿灯是13号引脚,被电机2占空比控制,因为灯是低电平点亮,因此电机2占空比为12,绿灯较亮,说明逻辑正确。

    可以看到位置差不多在47附近的时候,motor1的占空比为14,motor2的占空比为85,表示左转。

    此时红灯较亮,红灯是14号引脚,被电机1占空比控制,因为灯是低电平点亮,因此电机1占空比为14,红灯较亮,说明逻辑正确。

    小车的测试等待我回学校后更新!

    完整源码

    完整源码如下所示,大家可以复制该源码,进行测试,下一次学习笔记将会记录K210串口的配置,虽然往期教程已经做过了,但下期我将会重制一下,争取让代码更加好用,我们下期再见~!
    等下一期做完串口,K210这一系列可能不会日更,后期我会着手于写K210跑训练模型,以及怎么训练模型的教程,难度比现在来得大,因此更新的速度会放缓
    但我一定会保持质量,做到句句有注释,句句有原因,思路清晰, 喜欢我的教程的,希望能换到你们的一个关注。

    # Timer_V1.0 - By: FITQY - 周二 8 月 23 日 2022
    #__________________________________________________________________
    # 导入模块
    import sensor, time, image                                  # 导入感光元件模块 sensor 跟踪运行时间模块 time 机器视觉模块 image
    import utime                                                # 导入延时模块 utime
    from fpioa_manager import fm                                # 从 GPIO 模块中导入 引脚注册模块 fm
    from Maix import GPIO                                       # 从 Maix 模块中导入 模块 GPIO
    import lcd                                                  # 导入 LCD 模块
    from machine import Timer, PWM                              # 从 machine 模块中导入 定时器模块 Timer 脉宽调制模块 PWM
    
    #__________________________________________________________________
    # 感光元件设置
    sensor.reset()                                              # 重置并初始化感光元件 默认设置为 摄像头频率 24M 不开启双缓冲模式
    #sensor.reset(freq=24000000, dual_buff=True)                # 设置摄像头频率 24M 开启双缓冲模式 会提高帧率 但内存占用增加
    
    sensor.set_pixformat(sensor.RGB565)                         # 设置图像格式为 RGB565 (彩色) 除此之外 还可设置格式为 GRAYSCALE 或者 YUV422
    sensor.set_framesize(sensor.QVGA)                           # 设置图像大小为 QVGA (320 x 240) 像素个数 76800 K210最大支持格式为 VGA
    
    sensor.set_auto_exposure(1)                                 # 设置自动曝光
    #sensor.set_auto_exposure(0, exposure=120000)               # 设置手动曝光 曝光时间 120000 us
    
    sensor.set_auto_gain(0, gain_db = 12)                       # 设置画面增益 17 dB 影响实时画面亮度
    sensor.set_auto_whitebal(0, rgb_gain_db = (0,0,0))          # 设置RGB增益 0 0 0 dB 影响画面色彩呈现效果 在 K210 上无法调节增益 初步判定是感光元件 ov2640 无法支持
    
    #sensor.set_contrast(0)                                     # 设置对比度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释
    #sensor.set_brightness(0)                                   # 设置亮度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释
    #sensor.set_saturation(0)                                   # 设置饱和度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释
    
    sensor.set_vflip(1)                                         # 打开垂直翻转 如果是 01Studio 的 K210 不开启会导致画面方向与运动方向相反
    sensor.set_hmirror(1)                                       # 打开水平镜像 如果是 01Studio 的 K210 不开启会导致画面方向与运动方向相反
    
    sensor.skip_frames(time = 2000)                             # 延时跳过2s 等待感光元件稳定
    
    #__________________________________________________________________
    # 创建时钟对象
    clock = time.clock()                                        # 创建时钟对象 clock
    
    #__________________________________________________________________
    # 打印sensor参数
    def print_sensor():
        print("Exposure: "+str(sensor.get_exposure_us()))       # 打印 曝光时间
        print("Gain: "+str(sensor.get_gain_db()))               # 打印 画面增益
        print("RGB: "+str(sensor.get_rgb_gain_db()))            # 打印 RGB 增益
    
    #__________________________________________________________________
    # 目标点输入类 举例 对标 2022 年 TI 杯送货无人机 中的目标点输入部分
    class point_input():
        point1  = 0                                             # 目标点 1
        point2  = 0                                             # 目标点 2
        cross   = 0                                             # 穿圈模式标志位
        send    = 0                                             # 目标点发送标志位
    
    point = point_input()                                       # 实例化目标点输入类 point_input() 为 point
    
    # 按键控制下的目标点获取函数
    def point_control(ckey):
        if ckey.control == 1:                                   # 按键确认及发送控制标志位为1 即 按键3 按下
            ckey.control = 0                                    # 重置标志位
            if ckey.cs == 0:                                    # 如果当前为模式 0
                point.send = 1                                  # 目标点发送标志置为 1 串口开始发送
    
            elif ckey.cs == 1:                                  # 如果当前为模式 1
                point.point1 = ckey.cinput                      # 将按键输入值赋值给目标点 1
    
            elif ckey.cs == 2:                                  # 如果当前为模式 2
                point.point2 = ckey.cinput                      # 将按键输入值赋值给目标点 2
    
            elif ckey.cs == 3:                                  # 如果当前为模式 3
                point.cross = ckey.cinput                       # 将按键输入值赋值给 穿圈模式标志位
    
        if ckey.csflag == 1:                                    # 如果检测到按键模式切换
            ckey.csflag = 0                                     # 重置按键模式切换标志位
            ckey.cinput = 0                                     # 重置按键输入值
    
    #__________________________________________________________________
    # 按键的使用
    # 定义按键控制类
    class key_control():                                        # 定义按键控制类
        cnt     = 0                                             # 按键计数值
        cs      = 0                                             # 按键模式选择标志位
        csmax   = 0                                             # 按键模式上限
        csflag  = 0                                             # 按键模式切换标志位
        cinput  = 0                                             # 按键输入值保存位
        control = 0                                             # 按键确认及发送控制标志位
    
    # 实例化按键类
    key = key_control()                                         # 实例化按键控制类 key_control() 为 key
    key.csmax = 4                                               # 按键模式上限为 4 即最多有 4 个模式
    
    # 注册按键引脚
    fm.register(16, fm.fpioa.GPIOHS0, force = True)             # 配置 16 脚为 KEY0 使用高速 GPIO 口 强制注册
    fm.register(18, fm.fpioa.GPIOHS1, force = True)             # 配置 18 脚为 KEY1 使用高速 GPIO 口 强制注册
    fm.register(19, fm.fpioa.GPIOHS2, force = True)             # 配置 19 脚为 KEY2 使用高速 GPIO 口 强制注册
    fm.register(20, fm.fpioa.GPIOHS3, force = True)             # 配置 20 脚为 KEY3 使用高速 GPIO 口 强制注册
    
    # 创建按键对象
    KEY0 = GPIO(GPIO.GPIOHS0, GPIO.IN, GPIO.PULL_UP)            # 创建按键对象 KEY0
    KEY1 = GPIO(GPIO.GPIOHS1, GPIO.IN, GPIO.PULL_UP)            # 创建按键对象 KEY1
    KEY2 = GPIO(GPIO.GPIOHS2, GPIO.IN, GPIO.PULL_UP)            # 创建按键对象 KEY2
    KEY3 = GPIO(GPIO.GPIOHS3, GPIO.IN, GPIO.PULL_UP)            # 创建按键对象 KEY3
    
    # 中断回调函数 KEY0 控制按键模式选择
    def key0_switch(KEY0):
        utime.sleep_ms(10)                                      # 延时 10ms 消除按键抖动
        if KEY0.value() == 0:                                   # 确认 按键0 按下
            key.csflag = 1                                      # 标记按键模式切换
            if key.cs < key.csmax:                              # 控制按键模式选择 自增
                key.cs = key.cs + 1
            else:                                               # 若达到上限 则重新从 0 开始
                key.cs = 0
    
    # 中断回调函数 KEY1 按键输入值自增
    def key1_switch(KEY1):
        utime.sleep_ms(10)                                      # 延时 10ms 消除按键抖动
        if KEY1.value() == 0:                                   # 确认 按键1 按下
            key.cinput = key.cinput + 1                         # 按键输入值自增
    
    # 中断回调函数 KEY2 按键输入值自减
    def key2_switch(KEY2):
        utime.sleep_ms(10)                                      # 延时 10ms 消除按键抖动
        if KEY2.value() == 0:                                   # 确认 按键2 按下
            key.cinput = key.cinput - 1                         # 按键输入值自减
    
    # 中断回调函数 KEY3 按键确认及发送控制标志位
    def key3_switch(KEY3):
        utime.sleep_ms(10)                                      # 延时 10ms 消除按键抖动
        if KEY3.value() == 0:                                   # 确认按键按下
            key.control = 1                                     # 按键确认及发送控制标志位
    
    # 开启中断 下降沿触发
    KEY0.irq(key0_switch, GPIO.IRQ_FALLING)                     # 开启 按键0 外部中断 下降沿触发
    KEY1.irq(key1_switch, GPIO.IRQ_FALLING)                     # 开启 按键1 外部中断 下降沿触发
    KEY2.irq(key2_switch, GPIO.IRQ_FALLING)                     # 开启 按键2 外部中断 下降沿触发
    KEY3.irq(key3_switch, GPIO.IRQ_FALLING)                     # 开启 按键3 外部中断 下降沿触发
    
    #__________________________________________________________________
    # LCD 的使用
    # LCD 初始化
    lcd.init()                                                  # lcd初始化
    
    # LCD 按键信息及目标点信息显示函数
    def lcd_key():
        lcd.draw_string(0, 0,  "key_cs: "+str(key.cs), lcd.BLUE, lcd.WHITE)
        lcd.draw_string(0, 15, "cinput: "+str(key.cinput), lcd.BLUE, lcd.WHITE)
        lcd.draw_string(0, 30, "point1: "+str(point.point1), lcd.BLUE, lcd.WHITE)
        lcd.draw_string(0, 45, "point2: "+str(point.point2), lcd.BLUE, lcd.WHITE)
        lcd.draw_string(0, 60, "cross : "+str(point.cross), lcd.BLUE, lcd.WHITE)
        lcd.draw_string(0, 75, "red_cx: "+str(red.cx), lcd.BLUE, lcd.WHITE)
        lcd.draw_string(0, 90, "motor1: "+str(motor.motor1), lcd.BLUE, lcd.WHITE)
        lcd.draw_string(0, 105,"motor2: "+str(motor.motor2), lcd.BLUE, lcd.WHITE)
        lcd.draw_string(0, 120,"FPS   : "+str(clock.fps()), lcd.BLUE, lcd.WHITE)
    
    #__________________________________________________________________
    # LED 的使用
    # 注册LED引脚
    fm.register(14, fm.fpioa.GPIO2, force = True)               # 配置 14 脚为 LED_R 强制注册
    fm.register(13, fm.fpioa.GPIO1, force = True)               # 配置 13 脚为 LED_G 强制注册
    fm.register(12, fm.fpioa.GPIO0, force = True)               # 配置 12 脚为 LED_B 强制注册
    
    # 创建LED对象
    LED_R = GPIO(GPIO.GPIO2, GPIO.OUT)                          # 创建 LED_R 对象
    LED_G = GPIO(GPIO.GPIO1, GPIO.OUT)                          # 创建 LED_G 对象
    LED_B = GPIO(GPIO.GPIO0, GPIO.OUT)                          # 创建 LED_B 对象
    
    # LED控制函数
    def led_control(led_flag):                                  # LED控制函数 根据传入 led_flag 点亮对应的灯
        if led_flag == 0:                                       # 传入参数为 0 所有灯打开
            LED_R.value(0)
            LED_G.value(0)
            LED_B.value(0)
    
        elif led_flag == 1:                                     # 传入参数为 1 所有灯关闭
            LED_R.value(1)
            LED_G.value(1)
            LED_B.value(1)
    
        elif led_flag == 2:                                     # 传入参数为 2 红灯常亮
            LED_R.value(0)
            LED_G.value(1)
            LED_B.value(1)
    
        elif led_flag == 3:                                     # 传入参数为 3 绿灯常亮
            LED_R.value(1)
            LED_G.value(0)
            LED_B.value(1)
    
        elif led_flag == 4:                                     # 传入参数为 4 蓝灯常亮
            LED_R.value(1)
            LED_G.value(1)
            LED_B.value(0)
    
        else:                                                   # 其他情况 紫灯
            LED_R.value(0)
            LED_G.value(1)
            LED_B.value(0)
    
    #__________________________________________________________________
    # 定时器的使用
    # 定义定时器属性类
    class timer_property():
        cnt     = 0                                             # 定时器计数值
        cnt_max = 0                                             # 定时器计数值上限
        period  = 0                                             # 定时器周期
        freq    = 0                                             # 定时器频率
    
    # 定时器0 配置_______________________________________________________
    # 定时器0 实例化类
    timer0 = timer_property()                                   # 实例化定时器属性类 timer_property() 为 timer0
    timer0.cnt_max = 9                                          # 设定 定时器0 的计数值上限为 9
    timer0.period = 100                                         # 设定 定时器0 的周期为 100
    
    # 定时器0 定义回调函数
    def timer0_back(tim0):
        if timer0.cnt < timer0.cnt_max:                         # 若 定时器0 的计数值小于 定时器0 的计数值上限
            timer0.cnt = timer0.cnt + 1                         # 计数值自增
        else:
            timer0.cnt = 0                                      # 超出计数值上限 则计数值重置为0
    
    # 定时器0 初始化
    tim0 = Timer(Timer.TIMER0,                                  # 定时器编号 定时器0
                Timer.CHANNEL0,                                 # 定时器通道 通道0
                mode = Timer.MODE_PERIODIC,                     # 定时器模式 周期性
                unit = Timer.UNIT_MS,                           # 定时器周期单位 ms
                period = timer0.period,                         # 定时器周期 timer0.period 若 unit 为 Timer.UNIT_MS 则周期为 timer0.period ms
                callback = timer0_back)                         # 定时器触发中断后执行的回调函数 timer0_back
    
    # 定时器1 配置_______________________________________________________
    # 电机类定义
    class motor_property():
        motor1      = 0                                         # 电机1 占空比
        motor2      = 0                                         # 电机2 占空比
        motor3      = 0                                         # 电机3 占空比
        motor4      = 0                                         # 电机4 占空比
    
        motor1_pin  = 0                                         # 电机1 引脚
        motor2_pin  = 0                                         # 电机2 引脚
        motor3_pin  = 0                                         # 电机3 引脚
        motor4_pin  = 0                                         # 电机4 引脚
    
        control_x   = 0                                         # 被控坐标 x
        control_y   = 0                                         # 被控坐标 y
    
    # 实例化电机类
    motor = motor_property()                                    # 实例化电机类 motor_property() 为 motor
    motor.motor1 = 50                                           # 电机1的占空比 初始设置为 50%
    motor.motor2 = 50                                           # 电机2的占空比 初始设置为 50%
    motor.motor1_pin  = 14                                      # 电机1的引脚 14为红灯引脚 这里先用灯的亮灭观察效果
    motor.motor2_pin  = 13                                      # 电机2的引脚 13为绿灯引脚 这里先用灯的亮灭观察效果
    
    # 定时器1 实例化类
    timer1 = timer_property()                                   # 实例化定时器属性类 timer_property() 为 timer1
    timer1.freq = 1000                                          # 设定 定时器1 的频率为 1000
    
    # 定时器1 通道0 初始化
    tim1_ch0 = Timer(Timer.TIMER1,                              # 定时器编号 定时器1
                     Timer.CHANNEL0,                            # 定时器通道 通道0
                     mode = Timer.MODE_PWM)                     # 定时器模式 PWM
    
    # 定时器1 通道1 初始化
    tim1_ch1 = Timer(Timer.TIMER1,                              # 定时器编号 定时器1
                     Timer.CHANNEL1,                            # 定时器通道 通道1
                     mode = Timer.MODE_PWM)                     # 定时器模式 PWM
    
    # 创建对象 电机1 通道为 定时器1的通道0 频率为 定时器1的频率 占空比为 电机1的占空比 引脚为 电机1的引脚
    motor1 = PWM(tim1_ch0, freq = timer1.freq, duty = motor.motor1, pin = motor.motor1_pin)
    
    # 创建对象 电机2 通道为 定时器1的通道1 频率为 定时器1的频率 占空比为 电机2的占空比 引脚为 电机2的引脚
    motor2 = PWM(tim1_ch1, freq = timer1.freq, duty = motor.motor2, pin = motor.motor2_pin)
    
    # 定义电机占空比控制函数
    def motor_control(motor, x):
        val = 0
        if x < motor.control_x:                                 # 若 当前坐标 小于 被控坐标x 即当前状态小车在目标的 左边
            val = (motor.control_x - x) * 0.3125                # 获取坐标差值 并转换为 0~50 之间的值
            motor.motor1 = 50 - val                             # 减小 电机1 占空比 电机1为左电机 使小车右转
            motor.motor2 = 50 + val                             # 增大 电机2 占空比 电机2为右电机 使小车右转
    
        elif x > motor.control_x:                               # 若 当前坐标 大于 被控坐标x 即当前状态小车在目标的 右边
            val = (x - motor.control_x) * 0.3125                # 获取坐标差值 并转换为 0~50 之间的值
            motor.motor1 = 50 + val                             # 增大 电机1 占空比 电机1为左电机 使小车左转
            motor.motor2 = 50 - val                             # 减小 电机2 占空比 电机2为右电机 使小车左转
    
        motor.motor1 = int(motor.motor1)                        # 将 电机1占空比 转换为 整数
        motor.motor2 = int(motor.motor2)                        # 将 电机1占空比 转换为 整数
    
    #__________________________________________________________________
    # 寻找色块
    # 定义类
    class color_property():
        cx                      =  0                            # 色块 x轴 中心坐标
        cy                      =  0                            # 色块 y轴 中心坐标
        flag                    =  0                            # 色块标志位 1 找到 0 未找到
        color                   =  0                            # 色块颜色标志位 例如 你可以用 1 来表示 黑色
        density                 =  0                            # 色块密度比 反映色块锁定程度 值越大 锁定程度越好
        pixels_max              =  0                            # 色块像素最大值
        led_flag                =  0                            # LED标志位 方便调试用
    
        color_threshold         = (0, 0, 0, 0, 0, 0)            # 色块颜色阈值
        color_roi               = (0,0,320,240)                 # 色块寻找区域(感兴趣区域)
        color_x_stride          =  1                            # 色块 x轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
        color_y_stride          =  1                            # 色块 y轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
        color_pixels_threshold  =  100                          # 色块 像素个数阈值 例如调节此参数为100 则可以滤除色块像素小于100的色块
        color_area_threshold    =  100                          # 色块 被框面积阈值 例如调节此参数为100 则可以滤除色块被框面积小于100的色块
        color_merge             =  True                         # 是否合并寻找到的色块 True 则合并 False 则不合并
        color_margin            =  1                            # 色块合并间距 例如调节此参数为1 若上面选择True合并色块 且被找到的色块有多个 相距1像素 则会将这些色块合并
    
    # 实例化类
    # 黑色
    black = color_property()
    black.color_threshold         = (0, 50, -10, 10, -10, 10)
    black.color_roi               = (0,0,320,240)
    black.color_x_stride          =  1
    black.color_y_stride          =  1
    black.color_pixels_threshold  =  100
    black.color_area_threshold    =  100
    black.color_merge             =  True
    black.color_margin            =  1
    
    # 红色
    red   = color_property()
    red.color_threshold           = (0, 100, 20, 127, -10, 127)
    
    #red.color_roi                = (0,0,320,240)
    red.color_roi                 = (0,110,320,20)
    
    red.color_x_stride            =  1
    red.color_y_stride            =  1
    
    #red.color_pixels_threshold   =  100
    #red.color_area_threshold     =  100
    red.color_pixels_threshold    =  10
    red.color_area_threshold      =  10
    
    red.color_merge               =  True
    red.color_margin              =  1
    
    # 绿色 预留
    green = color_property()
    
    # 蓝色 预留
    blue  = color_property()
    
    # 定义寻找色块函数
    def opv_find_blobs(color,led_flag):
        color.pixels_max = 0                                    # 重置 色块 最大像素数量
        color.flag       = 0                                    # 重置 色块 标志位
        color.led_flag   = 0                                    # 重置 led 标志位
    
        for blobs in img.find_blobs([color.color_threshold],    # 色块颜色阈值
        roi = color.color_roi,                                  # 色块寻找区域(感兴趣区域)
        x_stride = color.color_x_stride,                        # 色块 x轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
        y_stride = color.color_y_stride,                        # 色块 y轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
        pixels_threshold = color.color_pixels_threshold,        # 色块 像素个数阈值 例如调节此参数为100 则可以滤除色块像素小于100的色块
        area_threshold = color.color_area_threshold,            # 色块 被框面积阈值 例如调节此参数为100 则可以滤除色块被框面积小于100的色块
        merge = color.color_merge,                              # 是否合并寻找到的色块 True 则合并 False 则不合并
        margin = color.color_margin):                           # 色块合并间距 例如调节此参数为1 若上面选择True合并色块 且被找到的色块有多个 相距1像素 则会将这些色块合并
            img.draw_rectangle(blobs[0:4])                      # 圈出找到的色块
            if color.pixels_max < blobs.pixels():               # 找到面积最大的色块
                color.pixels_max = blobs.pixels()
                color.cx = blobs.cx()                           # 将面积最大的色块的 x轴 中心坐标值 赋值给 color
                color.cy = blobs.cy()                           # 将面积最大的色块的 y轴 中心坐标值 赋值给 color
                color.flag = 1                                  # 标志画面中有找到色块
                color.density = blobs.density()                 # 将面积最大的色块的 色块密度比 赋值给 color
                color.led_flag = led_flag                       # 将控制led颜色的标志位的值 赋值给 color
    
        if color.flag == 1:                                     # 标记画面中被找到的最大色块的中心坐标
            img.draw_cross(color.cx,color.cy, color=127, size = 15)
            img.draw_circle(color.cx,color.cy, 15, color = 127)
    
    # 定义打印色块参数函数
    def print_blobs_property(color,name):
        print(name,"cx:",color.cx,"cy:",color.cy,"flag:",color.flag,"color:",color.color,"density:",color.density,"led_flag:",color.led_flag)
    
    #__________________________________________________________________
    # 调试区
    led_control(1)                                              # 关闭一下所有灯 再进入 while 循环 使显示结果正确
    
    #__________________________________________________________________
    # 主函数
    while(True):
    
        clock.tick()                                            # 跟踪运行时间
    
        img = sensor.snapshot()                                 # 拍摄一张照片
    
        #opv_find_blobs(black,1)                                # 找黑色色块 led标志为1 表示黑色
        opv_find_blobs(red,2)                                   # 找红色色块 led标志为2 表示红色
    
        point_control(key)                                      # 按键控制下的目标点获取函数
    
        lcd.display(img)                                        # LCD 显示图像
        lcd_key()                                               # LCD 显示按键信息及目标点信息
    
        #led_control(red.led_flag)                              # LED 标记色块识别情况
    
        motor.control_x = 160                                   # 控制目标处于 x轴中心点 160
        motor_control(motor,red.cx)                             # 电机占空比控制函数获取电机控制占空比
    
        motor1.duty(motor.motor1)                               # 将获取到的电机1占空比 装载
        motor2.duty(motor.motor2)                               # 将获取到的电机2占空比 装载
    
        if timer0.cnt == 0:                                     # 如果 timer0.cnt 等于 0 此步骤的目的是控制打印周期 不要打印的太快
            print_sensor()                                      # 打印sensor参数
            print_blobs_property(black,"Black-")                # 打印黑色色块参数
            print_blobs_property(red,  "Red-  ")                # 打印红色色块参数
    
    #__________________________________________________________________
    
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » 【K210】K210学习笔记四——定时器的使用

    发表评论