2019-12-22 13:50发布
Andrewz 发表于 2014-3-26 11:34 以前还真没见过. C语言的内容远远超出了课堂里老师涉及的内容. 学习了.
kalo425 发表于 2014-3-27 01:16 你好,我可不可以理解为 extern ARM_UART_DRIVER uart2Driver 这种用法降低了 C文件之间的耦合性。使移植 ...
gongxd 发表于 2014-3-27 08:42 呵呵 以为就在手边呢 谢谢 自己动手
最多设置5个标签!
思路很清晰 厉害
本身就是用来做模块化封装的技术。根据你的实际需求,按照某种方式划分的模块(比方说按照硬件划分,按照功能划分,甚至按照运行权限划分)最终采用资源结构体的方式对外提供一个接口。当接口固定时,即使需求发生变化也不用改动所有的代码,仅仅把变化的部分改掉就好,这个本来应该是编程习惯上的问题,但是显式的把接口写出来,并且把接口的说明写清楚,大家都依照这个接口来耦合上下层代码,不论是效率还是质量都会好于用习惯自律。
具体用的时候存在这样一个问题,本坛傻大师一直力主module.c里面不包含module.h,实际上本身.h就是给调用者使用模块的接口,我们这里讨论的例如ARM_UART_DRIVER这个结构体的定义应该就在这里。但是module.c不包含module.h的话又怎么获得这个结构体呢?傻大师给了一个掩码结构体的解决方案,你可以自己搜一下。至于成员函数,你包含到工程里面并且引用了这个函数就肯定会被编译并且链接啊。还是以extern ARM_UART_DRIVER uart2Driver为例子,实际上这只是个声明,告诉编译器外面有个形如ARM_UART_DRIVER的结构体,名字叫uart2Driver,至于在哪不知道,让编译器自己去找。但是你肯定要定义一个这样的结构体并且加入到工程当中去,否认编译器肯定要跟你抱怨找不到这个东西,找不到怎么用?找到的东西分两部分,一个是ARM_UART_DRIVER这是结构体定义,告诉编译器这个结构体长什么样子有哪些数据域,一个是uart2Driver是前面这个结构体的实例化,一般都会把每个数据域都填充有效的数据,这样直接拿来就可以用了:
举个栗子~
GPIO.h
里面有:
- typedef struct{
- u16 pinNumber;
- bool pinValue;
- GPIO_Type* port;
- (void)(*InterruptConfig)(void);
- (void) (*GPIO_IntertuptCallback)(bool enable);
- }GPIO_DRIVER;
复制代码GPIO.c里面有:
- const GPIO_DRIVER GPIO_P00 = {
- 0x00,//pinNumber
- 0x00,//pinValue
- GPIO_P0,
- P00_InterruptConfig,
- P00_ISR
- };
复制代码其中的GPIO_Type其实在这里根本用不上,只是为了说明一下,类似GPIO端口定义这样的内容一般会以
- typedef enum GPIO_Type
- {
- GPIO_P0,
- GPIO_P1
- 。。。
- };
复制代码这样的形式出现。
管脚编号和管脚值就是普通成员变量,不多说。(我不会告诉你们在cortexM3上面可以通过位带计算直接把管脚位指针放到这里面,这样每次都等于直接读管脚)
下面的中断配置按照ARM的风格是编写GPIO_InterruptConfig(IntInfo arg,bool enable),然后P00_InterruptConfig实际上是就是:
- void P00_InterruptConfig(bool enable)
- {
- GPIO_InterruptConfig(IntInfo_P00,enable);
- }
复制代码反正他就是这么写的,好不好见仁见智,我反正不喜欢~反正有更粗暴的办法来实现,我觉得这样多包一层真是。。。
类似的还有GPIO_Init,里面可以传一组在GPIO.h里面定义好的枚举参数用于配置管脚(比方说GPIO_P0这是端口,然后还有个端口的参数比分说叫GPIO_ConfigInfo,把参数应用到端口上就行了),这个忘记写进去了~
接下来是就是最厉害的了,callback是什么怎么用,请大家自行百度,反正从配置上面来讲,这里的中断函数体本身也是不给用户自己写的,反正开放了callback接口,把你要做的事情写进去,然后驱动会自动帮你完成中断匹配时调用你的处理函数的任务。
所以呢,最终用的时候,在user.c里面:
#include “GPIO.h”//获得封装模块的结构体信息
extern GPIO_DRIVER GPIO_P00;//把定义好的资源拿来用
然后就爽啦,直接GPIO_P00.Init(管脚配置.浮空输入);//反正就是这个意思,你懂的
读取呢就是(伪代码)bool pinValue = GPIO_P00.pinValue;
配置中断:GPIO_P00.P00_InterruptConfig(true);//传个true进去就是开中断,传个false进去就是关中断,中断优先级神马,这里肯定不管啦~
安装callback:GPIO_P00.P00_ISR = 你的中断处理函数;
void 你的中断处理函数(void)
{
干点什么;
}
这里的顺序肯定是先安装回调函数然后再配置中断。
PS:大早上的还没醒,随手打的里面说不定有错,反正意思就是这个意思,看起来为了一个管脚写这么多不划算,但是在实际中经过测试的驱动写一次就够了,整个团队乃至你的客户都可以用这个驱动最大的简化开发流程,用半导体厂商的话说就是“专注于应用开发”
看34L,顺便帮忙挑错~
小程序写多了,就跟吃饭喝水一样,自己趁手、舒服就行了,没必要麻烦的……面向对象不是任何地方都需要的:
我.喝水();
看起来简单,一行搞定,进去看看……里面还有喝水.厨房,喝水.茶,喝水.咖啡……厨房.插头,厨房.电水壶,厨房.杯子……茶.铁观音,茶.普洱……咖啡.雀巢,咖啡.蓝山……
所以我现在的做法就是先把项目做好整体规划,然后拷贝个模板,初始化代码一个函数里全搞定,方便检查,中断函数改一下,应用层也有不少跟硬件相关的,像读写IO口之类的顶多用宏封装一下……看似移植性比较差,但是现实中又有哪位尝试过把自己产品瞬间更换N种MCU……
100KB左右的bin(常量很少),尝试过在TI和ST的M3,以及芯唐的M0之间移植,速度还是很快的,摸索完新MCU的外设操作后,软件的移植也就两三天可以搞定。
一周热门 更多>