在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

相关文章

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解

Nginx添加内置模块过程

《Nginx添加内置模块过程》文章指导如何检查并添加Nginx的with-http_gzip_static模块:确认该模块未默认安装后,需下载同版本源码重新编译,备份替换原有二进制文件,最后重启服务验... 目录1、查看Nginx已编辑的模块2、Nginx官网查看内置模块3、停止Nginx服务4、Nginx

MySQL 数据库表操作完全指南:创建、读取、更新与删除实战

《MySQL数据库表操作完全指南:创建、读取、更新与删除实战》本文系统讲解MySQL表的增删查改(CURD)操作,涵盖创建、更新、查询、删除及插入查询结果,也是贯穿各类项目开发全流程的基础数据交互原... 目录mysql系列前言一、Create(创建)并插入数据1.1 单行数据 + 全列插入1.2 多行数据

MySQL 临时表创建与使用详细说明

《MySQL临时表创建与使用详细说明》MySQL临时表是存储在内存或磁盘的临时数据表,会话结束时自动销毁,适合存储中间计算结果或临时数据集,其名称以#开头(如#TempTable),本文给大家介绍M... 目录mysql 临时表详细说明1.定义2.核心特性3.创建与使用4.典型应用场景5.生命周期管理6.注

python urllib模块使用操作方法

《pythonurllib模块使用操作方法》Python提供了多个库用于处理URL,常用的有urllib、requests和urlparse(Python3中为urllib.parse),下面是这些... 目录URL 处理库urllib 模块requests 库urlparse 和 urljoin编码和解码

MySQL的触发器全解析(创建、查看触发器)

《MySQL的触发器全解析(创建、查看触发器)》MySQL触发器是与表关联的存储程序,当INSERT/UPDATE/DELETE事件发生时自动执行,用于维护数据一致性、日志记录和校验,优点包括自动执行... 目录触发器的概念:创建触www.chinasem.cn发器:查看触发器:查看当前数据库的所有触发器的定

创建springBoot模块没有目录结构的解决方案

《创建springBoot模块没有目录结构的解决方案》2023版IntelliJIDEA创建模块时可能出现目录结构识别错误,导致文件显示异常,解决方法为选择模块后点击确认,重新校准项目结构设置,确保源... 目录创建spChina编程ringBoot模块没有目录结构解决方案总结创建springBoot模块没有目录

idea Maven Springboot多模块项目打包时90%的问题及解决方案

《ideaMavenSpringboot多模块项目打包时90%的问题及解决方案》:本文主要介绍ideaMavenSpringboot多模块项目打包时90%的问题及解决方案,具有很好的参考价值,... 目录1. 前言2. 问题3. 解决办法4. jar 包冲突总结1. 前言之所以写这篇文章是因为在使用Mav