在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创建一个功能完整的Windows风格计算器程序

《使用Python创建一个功能完整的Windows风格计算器程序》:本文主要介绍如何使用Python和Tkinter创建一个功能完整的Windows风格计算器程序,包括基本运算、高级科学计算(如三... 目录python实现Windows系统计算器程序(含高级功能)1. 使用Tkinter实现基础计算器2.

Python中模块graphviz使用入门

《Python中模块graphviz使用入门》graphviz是一个用于创建和操作图形的Python库,本文主要介绍了Python中模块graphviz使用入门,具有一定的参考价值,感兴趣的可以了解一... 目录1.安装2. 基本用法2.1 输出图像格式2.2 图像style设置2.3 属性2.4 子图和聚

CentOS和Ubuntu系统使用shell脚本创建用户和设置密码

《CentOS和Ubuntu系统使用shell脚本创建用户和设置密码》在Linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设置密码,本文写了一个shell... 在linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设

使用Python和Pyecharts创建交互式地图

《使用Python和Pyecharts创建交互式地图》在数据可视化领域,创建交互式地图是一种强大的方式,可以使受众能够以引人入胜且信息丰富的方式探索地理数据,下面我们看看如何使用Python和Pyec... 目录简介Pyecharts 简介创建上海地图代码说明运行结果总结简介在数据可视化领域,创建交互式地

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

Python中的getopt模块用法小结

《Python中的getopt模块用法小结》getopt.getopt()函数是Python中用于解析命令行参数的标准库函数,该函数可以从命令行中提取选项和参数,并对它们进行处理,本文详细介绍了Pyt... 目录getopt模块介绍getopt.getopt函数的介绍getopt模块的常用用法getopt模

python logging模块详解及其日志定时清理方式

《pythonlogging模块详解及其日志定时清理方式》:本文主要介绍pythonlogging模块详解及其日志定时清理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录python logging模块及日志定时清理1.创建logger对象2.logging.basicCo

Qt spdlog日志模块的使用详解

《Qtspdlog日志模块的使用详解》在Qt应用程序开发中,良好的日志系统至关重要,本文将介绍如何使用spdlog1.5.0创建满足以下要求的日志系统,感兴趣的朋友一起看看吧... 目录版本摘要例子logmanager.cpp文件main.cpp文件版本spdlog版本:1.5.0采用1.5.0版本主要

idea中创建新类时自动添加注释的实现

《idea中创建新类时自动添加注释的实现》在每次使用idea创建一个新类时,过了一段时间发现看不懂这个类是用来干嘛的,为了解决这个问题,我们可以设置在创建一个新类时自动添加注释,帮助我们理解这个类的用... 目录前言:详细操作:步骤一:点击上方的 文件(File),点击&nbmyHIgsp;设置(Setti