打印
[应用方案]

MCU通用移植方案

[复制链接]
1443|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
1988020566|  楼主 | 2024-6-14 14:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
前言

因为项目的需求或者成本控制等因素,我们经常会遇到更换MCU的情况,这时我们可能需要将以前项目使用的代码移植到新的MCU上面。可能是同系列的不同型号,也可能是不同系列不同架构的MCU,对于一些新手来说,这个事情乍一看好像挺简单,但是上手之后又发现好像无从下手。我也经常收到一些关于移植问题的私信,所以这一期就大概讲一下如何从一款MCU移植到另一款MCU,大概讲一下方法和思路。

1 硬件移植

硬件层面上的移植其实是比较简单的,因为不管是什么单片机,在功能上其实基本差不多,比如GPIO,GPIO在所有单片机上面都是最基本的接口,而且基本一样,最常见的区别无非是能不能做外部中断,有些单片机是所有IO都可以做外部中断,有些则不是。其次就是一些外设常用的接口,如串口,IIC,SPI等,不同型号的单片机区别可能只是接口的数量,引脚号等等,这些在硬件上的使用基本一样,所以移植的时候按照原有的功能重新布线即可。

2 软件移植2.1 底层移植方法2.1.1 移植原理

底层移植顾名思义就是在原有的软件工程上,用新MCU的底层库替换掉原有旧的库,并且对原有的应用层做一些兼容性的修改,最后得到一个新的工程,示意图如下:

2.1.2 移植方法

1、修改系统时钟
系统时钟是MCU运行的基准,如果移植双方的时钟频率不一致,那么跟时间相关的一些函数就要做相应的改变才能保证原有的功能正常运行。比如串口波特率,I2C时钟频率,延时函数,定时器等,定时的时间是基于系统时钟计算出来的,如果系统时间变快了,这些定时的时间也会变短,像有些模拟I2C的驱动是用delay()函数来模拟I2C的时序,一旦这个延时时间变了,可能就直接导致I2C通讯异常。我们可以通过修改系统时钟,或者改变定时器分频等方法,让这部分的功能跟原有保持一致。
2、修改初始化函数
MCU在启动的时候都要先对各个模块进行初始化。比如GPIO初始化(包括时钟、引脚号、模式、速率等等)、I2C初始化,SPI初始化等等。不同型号的MCU用的可能是不一样的库,因此初始化这部分代码的写法上可能会有差异,这个时候我们还是按照功能移植的原则,把整个初始化部分替代掉。
当然,这个部分其实可以参考新固件库的demo,一般固件库都是有一些常用的Example,比如GPIO、UART、IIC、SPI、EXTI等等。我们可以先看下人家的demo是怎么用的,然后再按照自己的需求改。
3、修改底层接口
在应用或者驱动部分,我们经常会去调用一些底层的接口。比如GPIO拉高拉低,IIC读写、串口收发等等。
这些地方如果移植前后函数名或者用法不一致的话就需要修改成一致。主要是保证功能一致即可,命名或者入参这些其实不是最重要的。比如我在代码的某个地方需要用IIC发送一个数据,代码怎么改,结构怎改,这些不重要,只要能正确发出这个数据,那功能上就是一致的,就是成功替代过去了。
4、修改include包含
原有的工程文件在应用部分(c文件)会包含很多底层库的头文件(h文件),但因为移植了底层库进来,很多头文件的命名不一样,因此,这些也需要修改,和底层库保持一致。

2.2 中间层移植方法2.2.1 移植原理

在应用层和底层之间添加一个中间层作为上层和底层用来交互数据的接口通道,而这个中间层又可以匹配多个MCU的底层库,从而实现多种单片机之间的移植。
而且添加中间层以后,上层可以不用考虑底层的硬件实现,只需要做好应用部分的需求即可,而底层也不需要考虑上层复杂的逻辑关系,只需把每个硬件接口做好接口,这有利于项目的分工和细化。
层级关系如下所示:

2.2.2 移植方法

1、添加应用层访问接口
为了方便应用层访问和调度底层的各种硬件接口,中间层作为信号的中转站,最好是做一个通用的接口供应用层去调用,比如open(),read(),write(),set(),get()等,应用层可以通过open()函数打开和配置任意一个外设,可以通过read()获取到任意一个外设的数据。
2、封装底层外设接口
中间层需要把底层接口封装成通用API供应用层使用,而且针对不同的单片机,还需要对底层的接口做兼容,比如串口,I2C,SPI等外设接口,需要根据不同的底层库进行修改适配。
3、修改时间函数
时钟对于单片机运行至关重要,它是代码运行的一个基准,因此,为了保持应用层和底层的时间片一致,中间层应对时间做一个同步,比如软件延时,滴答时钟延时,定时器延时等,需要做到上下一致。
4、数据类型重定义
数据类型虽然都是按照编程语言的规定来定义的,但实际使用中,根据个人的使用习惯,往往会把常规的类型进行重定义,比如“unsigned char”,“ uchar”,“uint8_t”,“u8”等等。不同单片机的底层库也是一样,可能会使用不同的方式来表示,因此,为了在不修改应用层的基础上完成移植,中间层就需要对底层的数据类型进行重定义,保持和应用层一致。还有,为了方便上层对中间层的访问,也可以自定义一些结构体类型,比如可以定义一个设备句柄,用来指向具体的硬件外设,这样一来应用层就可以通过同一个API接口入参不同的设备句柄来访问不同的底层外设。

2.3 两种移植方法比对
移植方法
优点
缺点
适用范围

底层移植
移植操作简单直接,应用层可以直接访问底层,运行效率高
每次更换移植时,都需要对应用部分做改动
功能较为单一的项目

中间层移植
层次分明,应用层和底层之前互不干扰
上层和底层的交互需要中间层作为中转,运行效率相对来说低一些
功能多,应用部分逻辑复杂的项目
3 结束语

好了,关于MCU移植的介绍就讲到这里,本文其实讲的还是比较笼统的,不够全面,列举的例子也不多,实际的情况可能会更复杂,可能两个库之间差异很大,可能有些函数没看懂,不知道是做什么用的,可能在底层的基础上还有操作系统,文件结构更加复杂了,等等。这些情况我也没法一一列举,需要你根据实际情况去移植,但是基本原理是一样的,最终的目标都是功能替代,而实现的方法也不是唯一的,需要你灵活使用。


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

402

主题

10665

帖子

6

粉丝