126|0

22

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【Follow me第二季第4期】任务提交+遇到的问题及经验分享+心得体会 | Micropython [复制链接]

  本帖最后由 怀66 于 2025-1-10 21:17 编辑

前言:

        真的真的很荣幸能参加这次活动,首先感谢eeworld平台及平台里无私分享的大伙们。由于之前参加过一次followme,https://bbs.eeworld.com.cn/thread-1260076-1-1.html,用的circuitpython开发adafruit家的esp32,很方便,所以本次开发也选择了micropython作为开发,两者的区别不是很大,感兴趣可自行百度。

        非常遗憾的是,由于本人拿到板子后的这段时间一直很忙,另外我身边也没有其余的电子器件(焊枪,面包板,杜邦线,usb2ttl啥的),所以本次所有必做任务就只完成了基础要求,感觉愧对于平台给我的机会。当然虽然只是完成了最基本的内容,但是本人在开发过程中,依旧遇到了很多小问题,我就在此帖也写上遇到的问题及解决办法,希望能帮助遇上同样问题的小伙伴吧!

 

环境搭建:

        首先要使用micropython开发必须将板子刷上mpy固件,固件可以在很多地方找到,如arduino nano rp2040官网的指引手册,micropython的官网,还有就是使用openmv idle操作,当然我也会在帖子下附上固件。刷固件步骤,①板子上电连上电脑usb,可以看到闪灯程序自动运行;②快速连续按两下reset按键,等待电脑弹出一个u盘,如果电脑没有反应,两次按下按键的间隔可以长一点或者短一点,多试两次就可以了;③将固件.uf2文件复制到u盘里就可以了,然后按reset重启板子。(另外如果有跳线帽,也可以尝试先短接RFU和地后,一直按reset按键不松手,再上电,然后刷上uf2固件,官网也有流程,一般用前面的方法就行,我写在这里就是来凑字数的)。

        关于micropython的介绍我就不多赘述了,可以自己百度看看,也可以看01studio家的介绍,我一开始就是跟他家学的。micropython最方便的地方莫过于有一个repl实时交互解释器用作调试程序,很方便,但是注意这个repl是占用了一个串口的(我后面遇到的一个问题就和他有关),python语法也很友好,所以开发起来是不那么费脑子的,当然这也离不开前辈们写好的各种库,在这里再次感谢各位大佬提供的轮子,让我这条细狗也能跑起来哈哈哈。以下为任务提交。

使用物料:本贴只使用了开发板和一根microusb数据线

汇总视频:

汇总视频

 

①任务一:搭建环境并开启第一步Blink三色LED / 串口打印Hello DigiKey & EEWorld!

    说实话这个点灯任务我就遇到了大难题,因为我一开始看原理图没找到rgb灯连在单片机那个引脚,他这个原理图很搞笑的地方是,明明rgb是先通过nina模块再连到rp2040上的,但是他却把彩灯画在rp2040旁边,误导了我。

   

    在别的帖子的提醒下,我最终完成了任务。由于repl就是占用了串口,所以micropython的print函数就是将信息传输在串口上,我是用thonny作开发软件,底下那栏就是串口输出,当然你也可以用putty等软件查看哈或者官方推荐的openmv idle。

软件流程示意图:

 

from machine import Pin
import time

print(dir(Pin.board))   #打印引脚

led = Pin("LED", Pin.OUT)
blue = Pin("LED_BLUE", Pin.OUT)
green = Pin("LED_GREEN", Pin.OUT)
red = Pin("LED_RED", Pin.OUT)
led_list = [blue, green, red]
led.value(0)

while True:
    #三个彩灯轮流亮,小灯亮灭是交替的
    for i in led_list:
        status = False
        i.value(status)
        status = not status
        time.sleep(0.5)  #亮0.5s
        led.value(status)
        i.value(status)
        time.sleep(0.5)  #灭0.5s
        led.value(not status)
        print('Hello DigiKey & EEWorld!')


    

其中print(dir(Pin.board))   #打印引脚 是可以查看固件定义好的引脚参数信息,输出信息如下:

['__class__', '__name__', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'D19', 'D2', 'D20', 'D21', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'LED', 'LEDB', 'LEDG', 'LEDR', 'LED_BLUE', 'LED_BUILTIN', 'LED_GREEN', 'LED_RED', 'RX', 'SCL', 'SDA', 'TX', '__bases__', '__dict__']

可以看到,我们需要的LED,LEDB,LEDR,LEDG等等,十分友好方便我们使用。视频如下:

20241228_204219

 

任务二:学习IMU基础知识,调试IMU传感器,通过串口打印六轴原始数据;

    任务2是使用一个imu传感器,我上次followme也使用过类似的传感器,感兴趣可以去看https://bbs.eeworld.com.cn/thread-1255882-1-1.html

        然后读取六轴原始数据代码如下:(官方示例就有)

import time
from lsm6dsox import LSM6DSOX
from machine import Pin, I2C

# Initialize the LSM6DSOX sensor with I2C interface
lsm = LSM6DSOX(I2C(0, scl=Pin(13), sda=Pin(12)))

while True:
    # Read accelerometer values
    accel_values = lsm.accel()
    print('Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*accel_values))
    
    # Read gyroscope values
    gyro_values = lsm.gyro()
    print('Gyroscope:     x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*gyro_values))
    
    print("")
    time.sleep_ms(100)

由于上一次的followme,我就学习了实现姿态解算,所以这次我依旧也移植了代码。

rp2040负责读取6轴原始数据,然后根据姿态解算函数,解算出姿态角度,通过网络将角度信息发送到电脑服务端,电脑服务端收到信息后,通过虚拟串口软件,将信息通过虚拟串口送到上位机。

这么麻烦是因为,我用的上位机软件是别人编写好的,可以去我上次followme的帖子看,它只能接受串口数据,但是,由于我身边没有焊枪,没焊接排针,更没有usbtottl,就不能使用单片机的物理串口直接与上位机软件通信了。所以我就曲线救国,先通过网络,将信息传给电脑服务端,在用电脑的虚拟串口实现与上位机软件通信。电脑的服务端程序也是我自己编写的。

rp2040代码如下:

import time
from lsm6dsox import LSM6DSOX
from machine import Pin, I2C
from zitai_slove import Update_IMU
from q4 import IMUupdate
from One_filter import one_filter
import network,time
import socket


#WIFI连接函数
def WIFI_Connect():
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断

    if not wlan.isconnected():
        print('connecting to network...')
        wlan.connect('HuaiYe', 'huaiyeshuai') #输入WIFI账号密码

        while not wlan.isconnected():
            #超时判断,30秒没连接成功判定为超时
            if time.time()-start_time > 30 :
                print('WIFI Connected Timeout!')
                break
            
        #串口打印信息
        print('连接好了wifi')
        print('network information:', wlan.ifconfig())
        
WIFI_Connect()
s = socket.socket()
addr=('192.168.137.1',9999) 
s.connect(addr)

# Initialize the LSM6DSOX sensor with I2C interface
lsm = LSM6DSOX(I2C(0, scl=Pin(13), sda=Pin(12)))

# def getdata():
#     accel_values = lsm.accel()
#     gyro_values = lsm.gyro()
#     return accel_values+gyro_values
# 
# acc_gyro = getdata()

while True:
    acc_x, acc_y, acc_z = [value for value in lsm.accel()]
    gyro_x, gyro_y, gyro_z = [value for value in lsm.gyro()]
    zitai = Update_IMU(acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z)
    #     zitai = IMUupdate(acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z) #q4
    #     zitai = one_filter(acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z)  #one_filter
#     print(zitai)
    s.send(f'pitch:{zitai[0]}, roll:{zitai[1]}, yaw:{zitai[2]}')
#     zitai = bytearray(f'pitch:{zitai[0]}, roll:{zitai[1]}, yaw:{zitai[2]}')     #pitch:, roll:, yaw:
    time.sleep(0.01)

 

 

姿态解算代码其一如下:(我会在文末一并上传)

#coding:utf-8

import math

#IMU算法更新


Kp = 100 #比例增益控制加速度计/磁强计的收敛速度
Ki = 0.002 #积分增益控制陀螺偏差的收敛速度
halfT = 0.001 #采样周期的一半

#传感器框架相对于辅助框架的四元数(初始化四元数的值)
q0 = 1
q1 = 0
q2 = 0
q3 = 0

#由Ki缩放的积分误差项(初始化)
exInt = 0
eyInt = 0
ezInt = 0

def Update_IMU(ax,ay,az,gx,gy,gz):
    global q0
    global q1
    global q2
    global q3
    global exInt
    global eyInt
    global ezInt
    # print(q0)
    
    #测量正常化
    norm = math.sqrt(ax*ax+ay*ay+az*az)
    #单元化
    ax = ax/norm
    ay = ay/norm
    az = az/norm
    
    #估计方向的重力
    vx = 2*(q1*q3 - q0*q2)
    vy = 2*(q0*q1 + q2*q3)
    vz = q0*q0 - q1*q1 - q2*q2 + q3*q3
    
    #错误的领域和方向传感器测量参考方向之间的交叉乘积的总和
    ex = (ay*vz - az*vy)
    ey = (az*vx - ax*vz)
    ez = (ax*vy - ay*vx)
    
    #积分误差比例积分增益
    exInt += ex*Ki
    eyInt += ey*Ki
    ezInt += ez*Ki
    
    #调整后的陀螺仪测量
    gx += Kp*ex + exInt
    gy += Kp*ey + eyInt
    gz += Kp*ez + ezInt
    
    #整合四元数
    q0 += (-q1*gx - q2*gy - q3*gz)*halfT
    q1 += (q0*gx + q2*gz - q3*gy)*halfT
    q2 += (q0*gy - q1*gz + q3*gx)*halfT
    q3 += (q0*gz + q1*gy - q2*gx)*halfT
    
    #正常化四元数
    norm = math.sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3)
    q0 /= norm
    q1 /= norm
    q2 /= norm
    q3 /= norm
    
    #获取欧拉角 pitch、roll、yaw
    pitch = math.asin(-2*q1*q3+2*q0*q2)*57.3
    roll = math.atan2(2*q2*q3+2*q0*q1,-2*q1*q1-2*q2*q2+1)*57.3
    yaw = math.atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3)*57.3
    return pitch,roll,yaw

 

电脑服务端代码如下:

import socket
import serial
import sys
import time

#创建串口
ser = serial.Serial()
ser.port = 'COM1'      
ser.baudrate = 115200
ser.open()

# 创建 socket 对象
serversocket = socket.socket(
            socket.AF_INET, socket.SOCK_STREAM)

port = 9999
# 绑定端口号
serversocket.bind(('192.168.137.1', port))

# 设置最大连接数,超过后排队
serversocket.listen(5)
# 建立客户端连接
print('\n'+'{:-^30}'.format('等待客户连接'))
clientsocket,addr = serversocket.accept()      
print("连接地址: %s" % str(addr))
# print(clientsocket.recv(1024))
msg='HELLO WORLD! By HuaiYe怀也\r\n'
msg_back = 0
clientsocket.send(msg.encode('utf-8'))

while True:
    msg_back = clientsocket.recv(1024)
    ser.write(msg_back)
    # print(msg_back.decode('utf-8'))
    
clientsocket.close()

虚拟串口软件是Configure Virtual Serial Port Driver。

视频如下:

20241228_205202

可以看出,效果拉跨,和别的大佬做的比起来,我就是依托答辩。

另外,这个任务遇到了一个很大的难题,那就是使用micropython固件连接网络,需要升级nina模块的固件,我在官网找到了升级教程,下载了arduino idle,使用了固件升级工具尝试了无数次,都没有成功。然后我就去arduino论坛翻啊翻,也没有解决。

最后根据我自己的尝试和推算,我终于发现了问题所在,那就是micropython的repl占用了与电脑usb通信的串口,所以固件一直刷不到nina上。解决办法是,找到一个不是micropython固件的.uf2文件,刷上去,然后再升级固件就可以了,最后再重新刷上micropython固件就可以使用了。

整个流程的示意图:

 

 

任务三:学习PDM麦克风技术知识,调试PDM麦克风,通过串口打印收音数据和音频波形:

    这个任务我是一点头绪都没有,但是已经有大佬造的轮子了,有大佬写好了相应的驱动库了,我们会用就行了,而且还有另外的大佬已经搬运到eeworld论坛了,所以我主要参考的就是HonestQiao大佬的https://bbs.eeworld.com.cn/thread-1299974-1-1.html#pid3381260,里面有提到github上大佬写好的库,我去github上下载好库,然后用示例代码就可以跑起来了,自己稍微修改了点点。

代码如下:

from time import sleep
from wavsimple import wav
from machine import Pin
import st34dt05a as pdm

pcm_rate = 8_000 # Hz - default is 12kHz i.e. 3.072MHz bit-sample rate
pdm.bit_sample_freq = pcm_rate * 256

pdm_clk = Pin(23)
pdm_data = Pin(22)

w = wav('output.wav', SampleRate=pcm_rate)
record_flag = False

def buffer_handler(inactive_buf):
    global record_flag
    if record_flag:
        buff = pdm.get_buffer(inactive_buf)
        w.write(buff) #录制声音
        for i,n in enumerate(buff): #答应数据,隔100打印一次
            if i % 100 == 0:
                print(n)   

pdm.init(pdm_clk, pdm_data, handler=buffer_handler)
pdm.start()

sleep(1) # wait whilst StateMachine inits

record_flag = True
sleep(10)
record_flag = False

w.close()

可以用thonny的绘图器查看波形,我也按照一定间隔才打印出来,避免占用板子过多资源。然后程序会生成output.wav文件,我们可以用电脑播放。

流程示意图:

 

视频如下:

20241229_113723

但是我有一个疑问,就是为什么录制的声音像是开了二倍数一样?有没有懂得大佬可以解释。

打包的代码和库:

作业提交代码.zip (33.83 KB, 下载次数: 4)

 

 

总结:

    这次完成的不是很好,真是愧对于平台给我的机会啊。

 

点赞 关注
 
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表