在mm32f3270上为micropython创建SPI模块

2024-02-28 13:32

本文主要是介绍在mm32f3270上为micropython创建SPI模块,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在mm32f3270上为micropython创建SPI模块

文章目录

  • 在mm32f3270上为micropython创建SPI模块
    • Introduction
    • Architecture
    • extmod/machine_spi.c
      • 软件SoftSPI
      • 硬件SPI
    • ports/xx/machine_spi.c

Introduction

在machine中实现SPI,我看各家的代码都引用了extmod/machine_spi.c中的模型,看来我也跑不掉,那就不要自创武功,随大流就好。

大体看了一下代码,预计工作量会在三个方面:

  • 了解extmod/machine_spi.c中对SPI的定义(SPI和SoftSPI)
  • 搞清楚需要port/mm32/machine_spi.c中实现的部分,做好适配
  • 处理好两个machine_spi.c,以及同平台的依赖关系,似乎要做一个hal_pin的适配

Architecture

在这个machine_spi.c中,很明确地要求在工程中启用宏"MICROPY_PY_MACHINE_SPI"。
OK,我也在mm32的mpconfigport.h中增加这个宏定义。

首先,我要把已经验证好的hal_spi.h和hal_spi.c文件放到mm32的驱动代码目录下面,在mm32的ports目录下创建machine_spi.h和machine_spi.c文件,可以预期不会再加更多的源文件了,只是在现有文件结构下改代码。

在具体代码的实现上,我将参考我已实现的machine_uart为参考,处理绑定引脚、多实例的具体问题。

然后在makefile增加新文件。

注意,SRC_C中的 $(SRC_MOD),不是在ports目录下的makefile中定义的,而是在extmod.mk文件中,然而,此处并没有包含extmod/machine_spi.c,所以仍需要手工引入。但阅读了别的包含machine_spi的makefile缺没却没有显示引用extmod的这个文件。算了,启动一次编译,看build log里有没有处理这个文件。
在这里插入图片描述
果然,build默认会把extmod全都过一遍,在启用MICROPY_PY_MACHINE_SPI的时候,extmod/machine_spi.c的代码正式起作用,送入编译器开编。

后续我根据报错的需求,不断添加代码,尽量确保编译通过。可以想见,再次全部编译通过的时候,也即将是移植完成。

extmod/machine_spi.c

之前的试编译中报错,提示在“mp_machine_soft_spi_print”函数中,有个“MP_HAL_PIN_FMT”没有定义。这里就要说道一下了。

extmod/machine_spi.c中有两部分:硬件SPI和软件SoftSPI。虽然我们希望使用硬件SPI,但因为micropython将这俩货放在一起,我们就仍要顺带完成SoftSPI的移植。

在stm32的modmachine.c中对machine模块内部的类定义时,有如下代码:

    #if MICROPY_PY_MACHINE_SPI{ MP_ROM_QSTR(MP_QSTR_SPI),                 MP_ROM_PTR(&machine_hard_spi_type) },{ MP_ROM_QSTR(MP_QSTR_SoftSPI),             MP_ROM_PTR(&mp_machine_soft_spi_type) },#endif

也就是ports目录下的machine_spi.c实现的machine_hard_spi_type,将会被当做SPI注册到系统中,而extmod/machine_spi.c中实现的mp_machine_soft_spi_type将使用SoftSPI这个名字。

从文章的顺序上,这里继读extmod/machine_spi.c的内容,但如果是调代码,这里就已经要在ports/xx/machine_spi.c中写一些代码框架了。

extmod/machine_spi.c的代码中通过代码注解描述了两个部分:

...
/******************************************************************************/
// MicroPython bindings for generic machine.SPI
.../******************************************************************************/
// Implementation of soft SPI
...

我曾经有念头,想先把SoftSPI的部分注释掉,仅实现纯硬件SPI的部分。。。算了,估计后面SoftI2C可能也要用,跑得了和尚跑不了庙,一起看吧。


STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {mp_obj_base_t *s = (mp_obj_base_t *)MP_OBJ_TO_PTR(args[0]);mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t *)s->type->protocol;spi_p->init(s, n_args - 1, args + 1, kw_args);return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init);

关于硬件SPI,最基本的是machine_spi_init()、machine_spi_deinit()和machine_spi_transfer()三个函数,这三个函数的实现都是调用s->type->protocol中注册的一堆函数。(在后面阅读代码时可以看到,这三个函数的底层都是在ports/xx/machine_spi.c中实现的,然后包装起来封装到protocol中装入mp_machine_spi_p_t类型的结构体中,然后再由ports/xx/machine_spi.c中引用组装成类模块。本文件中的spi_read()、spi_readinto()、spi_write()、spi_write_readinto()都是基于spi_transfer()定义的。

这里相当于是实现了SPI的一组通用操作的函数,但向上和向下约定了SPI的实现接口。

硬件SPI对底层的要求比较简单,软件SoftSPI相当于是对Pin模型的一个封装,稍微有点麻烦。但其实因为跟此处主题关联不大,所以在时间紧急的情况下,看似无关的工作量容易让人烦躁。

软件SoftSPI

软件SoftSPI直接操作GPIO引脚实现SPI通信。

baudrate_from_delay_half()函数,以500k作为目标频率,将传入的delay_half转换成delay的重复次数。
baudrate_to_delay_half()函数,以500k作为目标频率,将传入的baudrate转换成delay_half的时长。
这两个函数在搞什么鬼?

在mp_machine_soft_spi_print()函数中,mp_machine_soft_spi_obj_t是在extmod/machine_spi.h中定义的,可以不用管。然后就是“MP_HAL_PIN_FMT”,这是之前试编译出现的问题点,看一下别家的代码怎么实现的。

mimxrt在自己的mphalport.h中,包含了所有关于mp_hal_pin的定义,我也拿过来参考一下。刚好mm32的port中也有“machine_pin_obj_t”的定义,相似度最近。注意,随手要加上对machine_pin.h的包含,否则会报错找不到machine_pin_obj_t的定义。

仅仅加了四行代码就能编译通过了,WF。

#define MP_HAL_PIN_FMT          "%q"
#define mp_hal_pin_obj_t        const machine_pin_obj_t *
#define mp_hal_get_pin_obj(o)   pin_find(o)
#define mp_hal_pin_name(p)      ((p)->name)

mp_machine_soft_spi_make_new()函数内部调用的mp_hal_get_pin_obj()实际调用的是pin_find() 函数,而我在mm32上实现的pin_find()函数是兼容mimxrt的,所以可以无缝对接。兼容真香啊。

但这里仅仅是做了个接口定义,对引脚翻转的操作都放在了drivers/bus/softspi.c中,这里的mp_hal_pin_write()、mp_hal_pin_output()、mp_hal_delay_us_fast()等函数我在mm32中都没实现,怎么就编通了?

在py/mphal.h中有这么一段代码解释了我的疑问:

// If port HAL didn't define its own pin API, use generic
// "virtual pin" API from the core.
#ifndef mp_hal_pin_obj_t
#define mp_hal_pin_obj_t mp_obj_t
#define mp_hal_get_pin_obj(pin) (pin)
#define mp_hal_pin_read(pin) mp_virtual_pin_read(pin)
#define mp_hal_pin_write(pin, v) mp_virtual_pin_write(pin, v)
#include "extmod/virtpin.h"
#endif

但我已经定义了mp_hal_pin_obj_t了!!!可能之前编译好的模块乱入了,我要试着重新编译一下。还有mp_hal_pin_output还是要实现一下的。

重新编译竟然还可以通过???我在map文件中也没找到有用的信息。

试着把image下载到板子上运行一下,竟然也没有hardfault。怪哉。

好吧,之前一直担心软件SoftSPI乱入,至少现在暂时不用考虑SoftSPI,可以将注意力集中在硬件SPI。

硬件SPI

忍不了了,终于可以畅快地在ports/xx/machine_spi.c 中写代码了。

ports/xx/machine_spi.c

先参考esp8266的machine_hspi.c实现一个代码框架。设法打通调用路径再插代码。

呵呵,我在esp8288的实现中发现了一个小秘密:

    if (dest == NULL) {// fast case when we only need to write datasize_t chunk_size = 1024;size_t count = len / chunk_size;size_t i = 0;for (size_t j = 0; j < count; ++j) {for (size_t k = 0; k < chunk_size; ++k) {spi_tx8fast(HSPI, src[i]);++i;}ets_loop_iter();}while (i < len) {spi_tx8fast(HSPI, src[i]);++i;}// wait for SPI transaction to completewhile (spi_busy(HSPI)) {}}

这里竟然可以用轮询模式啊。。。

我考虑的是,有一种情况,SPI作为单工模式工作时,类似于UART串口的只发不收和只收不发,软件只要把数塞给缓冲区就好了,由硬件自己搞。但再想想,SPI的接收也是由发送驱动的,全部是主动控制,用轮询问题不大。实际上UART的发送就是轮询实现的。如果是这样,后面的I2C也可以考虑类似的实现。

这里,我先在machine_spi.h中定义了一个machine_hw_spi_obj_t结构体。。。对应的引脚映射后面再补。。。相对于extmod/machine_spi.c,这里实现的spi都是“hw_spi”

一顿操作猛如虎。

编译一下,终于还是报错了。

LINK build-MB_F3270/firmware.elf
C:\msys64\usr\gcc-arm-none-eabi-10-2020-q4-major\bin\arm-none-eabi-ld.exe: build-MB_F3270/extmod/machine_spi.o: in function `mp_machine_soft_spi_transfer':
machine_spi.c:(.text.mp_machine_soft_spi_transfer+0x2): undefined reference to `mp_soft_spi_transfer'
C:\msys64\usr\gcc-arm-none-eabi-10-2020-q4-major\bin\arm-none-eabi-ld.exe: build-MB_F3270/extmod/machine_spi.o: in function `mp_machine_soft_spi_init':
machine_spi.c:(.text.mp_machine_soft_spi_init+0x62): undefined reference to `mp_soft_spi_ioctl'
C:\msys64\usr\gcc-arm-none-eabi-10-2020-q4-major\bin\arm-none-eabi-ld.exe: build-MB_F3270/extmod/machine_spi.o: in function `mp_machine_soft_spi_make_new':
machine_spi.c:(.text.mp_machine_soft_spi_make_new+0x7c): undefined reference to `mp_soft_spi_ioctl'
make: *** [Makefile:200: build-MB_F3270/firmware.elf] Error 1

mp_soft_spi_transfer()等三个函数找不到?估计没包进来。

追了一下代码,这几个函数是在drivers/bus/softspi.c中定义的,还是需要在makefile中把这个文件加进来。但这个文件中引用了了mp_pin_hal_xxxx的一堆宏函数。

算了,洗洗睡了,明天再搞。


先做一点体力活,把F3270的SPI引脚清单做出来,逐步进入状态。

查阅pin_info.xlsx文件,在mm32/boards/mb_f3270/machine_pin_board_pins.c中添加关于SPI的记录。搞定。

在stm32的makefile中,查找“softspi”关键字,找到了“driver_src_c”的定义,看来需要显式在makefile 增加drivers 目录下的文件。


DRIVERS_SRC_C += $(addprefix drivers/,\bus/softspi.c \)SRC_C += \main.c \...$(DRIVERS_SRC_C) \

再编一次。果然开始报错说找不到mp_hal_pin_xxx的一堆函数了。

在这里插入图片描述
参照mimxrt在mphalport.h的实现,也适配一下。

这里看到还有mp_hal_pin_open_drain()的实现,暂时不用管,可能是给后面SoftI2C用的。


#define mp_hal_pin_write(p, value) (GPIO_WriteBit(p->gpio_port, (1u << p->gpio_pin), value))
#define mp_hal_pin_read(p)  (GPIO_ReadInputDataBit(p->gpio_port, (1u << p->gpio_pin)))
#define mp_hal_pin_output(p) machine_pin_set_mode(p, PIN_MODE_OUT_PUSHPULL);
#define mp_hal_pin_input(p)  machine_pin_set_mode(p, PIN_MODE_IN_PULLUP);...#define mp_hal_delay_us_fast(us) mp_hal_delay_us(us)

在mm32/mphalport.h文件中添加了一些代码,同时在machine_pin.c文件中添加了machine_pin_set_mode()函数的实现。

再编一次。编通了。
在这里插入图片描述
顺便启用了SoftSPI。重新编译,现在到板子里试一下。

在这里插入图片描述
可以导入SPI模块,并且能dir到内部属性,但实例化的语句会触发hardfault。

初步分析是machine_hw_spi_make_new()函数中的动态内存分配代码导致的hardfault。

    machine_hw_spi_obj_t *self = m_new_obj(machine_hw_spi_obj_t);self->base.type = &machine_hw_spi_type;

这个m_new_obj()函数最终调用了m_malloc()函数。。。关于动态内存分配的事情,这里暂时先不考虑。

按照以往的做法,用hw_spi_find()函数取代这里的new函数。

const machine_hw_spi_obj_t *hw_spi_find(mp_obj_t user_obj)
{/* 如果传入参数本身就是一个uart的实例,则直接送出这个UART。 */if ( mp_obj_is_type(user_obj, &machine_hw_spi_type) ){return user_obj;}/* 如果传入参数是一个uart通道号,则通过索引在UART清单中找到这个通道,然后送出这个通道。 */if ( mp_obj_is_small_int(user_obj) ){uint8_t idx = MP_OBJ_SMALL_INT_VALUE(user_obj);if ( idx < MACHINE_HW_SPI_NUM ){return machine_hw_spi_objs[idx];}}mp_raise_ValueError(MP_ERROR_TEXT("HW SPI doesn't exist"));
}

然后创建了一些函数用以完成硬件SPI的适配。。。到凌晨了,刚准备好代码,勉强调通了,还没测。不想写笔记了,洗洗先睡,明天再搞。


昨天午夜完成了一个版本,今天到办公室用逻辑分析仪测了一下,修复了micropython中SPI的相位设置同mm32底层驱动设置不一致的问题。

目前可以使用SPI模块的write()和read()方法进行读写。但是readinto()和write_readinto()函数在工作时仍然会抛异常,说是需要提供带buffer的协议。

>>> arr2=[]
>>> spi0.write_readinto(arr, arr2)
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: object with buffer protocol required

我估计还是在移植micropython内核时没处理好动态内存分配malloc的问题。抽空还是要专门解决掉。

END

这篇关于在mm32f3270上为micropython创建SPI模块的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755637

相关文章

MySQL 用户创建与授权最佳实践

《MySQL用户创建与授权最佳实践》在MySQL中,用户管理和权限控制是数据库安全的重要组成部分,下面详细介绍如何在MySQL中创建用户并授予适当的权限,感兴趣的朋友跟随小编一起看看吧... 目录mysql 用户创建与授权详解一、MySQL用户管理基础1. 用户账户组成2. 查看现有用户二、创建用户1. 基

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Macos创建python虚拟环境的详细步骤教学

《Macos创建python虚拟环境的详细步骤教学》在macOS上创建Python虚拟环境主要通过Python内置的venv模块实现,也可使用第三方工具如virtualenv,下面小编来和大家简单聊聊... 目录一、使用 python 内置 venv 模块(推荐)二、使用 virtualenv(兼容旧版 P

一文彻底搞懂Java 中的 SPI 是什么

《一文彻底搞懂Java中的SPI是什么》:本文主要介绍Java中的SPI是什么,本篇文章将通过经典题目、实战解析和面试官视角,帮助你从容应对“SPI”相关问题,赢得技术面试的加分项,需要的朋... 目录一、面试主题概述二、高频面试题汇总三、重点题目详解✅ 面试题1:Java 的 SPI 是什么?如何实现一个

Linux lvm实例之如何创建一个专用于MySQL数据存储的LVM卷组

《Linuxlvm实例之如何创建一个专用于MySQL数据存储的LVM卷组》:本文主要介绍使用Linux创建一个专用于MySQL数据存储的LVM卷组的实例,具有很好的参考价值,希望对大家有所帮助,... 目录在Centos 7上创建卷China编程组并配置mysql数据目录1. 检查现有磁盘2. 创建物理卷3. 创

Dubbo之SPI机制的实现原理和优势分析

《Dubbo之SPI机制的实现原理和优势分析》:本文主要介绍Dubbo之SPI机制的实现原理和优势,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Dubbo中SPI机制的实现原理和优势JDK 中的 SPI 机制解析Dubbo 中的 SPI 机制解析总结Dubbo中

Python logging模块使用示例详解

《Pythonlogging模块使用示例详解》Python的logging模块是一个灵活且强大的日志记录工具,广泛应用于应用程序的调试、运行监控和问题排查,下面给大家介绍Pythonlogging模... 目录一、为什么使用 logging 模块?二、核心组件三、日志级别四、基本使用步骤五、快速配置(bas

Java 如何创建和使用ExecutorService

《Java如何创建和使用ExecutorService》ExecutorService是Java中用来管理和执行多线程任务的一种高级工具,可以有效地管理线程的生命周期和任务的执行过程,特别是在需要处... 目录一、什么是ExecutorService?二、ExecutorService的核心功能三、如何创建