X
首页
技术
模拟电子
单片机
半导体
电源管理
嵌入式
传感器
最能打国产芯
应用
汽车电子
工业控制
家用电子
手机便携
安防电子
医疗电子
网络通信
测试测量
物联网
最能打国产芯
大学堂
首页
直播
专题
TI 培训
论坛
汽车电子
国产芯片
电机驱动控制
电源技术
单片机
模拟电子
PCB设计
电子竞赛
DIY/开源
嵌入式系统
医疗电子
颁奖专区
【厂商专区】
【电子技术】
【创意与实践】
【行业应用】
【休息一下】
最能打国产芯
活动中心
直播
发现活动
颁奖区
电子头条
参考设计
下载中心
分类资源
文集
排行榜
电路图
Datasheet
最能打国产芯
编程基础
[讨论] 只为uC而生,uS成长历程4
辛昕
2013-8-2 19:58
楼主
昨晚我花了不少时间,做了一系列关于开销的测试。
我们可以得出一个我们很关心的结论:
那就是,相比于平常的直接调用变量和函数。
采用指针,乃至通过结构体引用,相比而言,多出来的 时间开销并不是十分大,大多数情况下,不足以成为影响效率和实时性的问题。
故而,
从此开始,我们不再有任何这方面的忧虑,可以大胆的使用这种方法。
而前面也说了。相比于时间开销,一个更大的问题应该是 作为使用者,用起来会比较辛苦。
复杂的使用方法,尽管不是一个硬伤,但是,它仍然有违我们的初衷。
然而,我们也分析过使用函数指针调用代替 直接调用 的 一些好处,不好放弃。
于是,我们试图重新梳理实现思路,尽可能简化使用的方式。
点赞
回复评论 (17)
沙发
辛昕
uSCore的全局考虑
尽管我前面一步一步从定时器开始,是为了强调一种思路,为了提高编程的执行力,我们不要考虑的太多,而是要尽可能行动。
而实质上,我早在开始写这一系列的帖子以前,我已经全盘考虑过整个uS的大致构成。
其中,对于uSCore。
截至目前,我考虑实现的两个部件是
1.定时器中断;
2.数据收发功能(目前将以串口实现);
定时器中断的基本功能是:
1.作为一个异步定时器;
2.预留一个粗糙的时间片轮训机制的实现基础;
而串口收发功能 是为了实现 可以直接从uSCore内部向外部输出信息。
这和我们直接在Apper里实现的区别是什么呢?
区别主要是:
1.并不是所有的uC应用程序都需要串口。(当然,这个可以根据实际实现情况,换成其他数据口,比如以太网口或者无线通信接口)
这里似乎有点强制Apper实现。
它的功能如同C标准里的stderr流,用来输出错误信息。
2.把它实现在uSer内部,可以把包括向外输出uSer错误或者其他状态信息的功能都集中到uSer里,而无需Apper多花精力去实现。
两个主要部件,意味着至少要带入两个函数指针,也可能不止如此。
此外,我们不能不考虑将来uSCore的功能部件可能扩展。
此外,作为uSer的最重要逻辑块,也就是我们的 uS_Pheriphal,即大家最关心的 外围设备的通用接口。
它们无论如何逃不了若干个IO函数。
因此可以想见,这些函数指针的数量极其繁多。
因此,通过单一一个函数指针传递是不现实的。
因此我们考虑一种新的封装方法。
我们把这些函数指针全部封装成一个新的结构体。
通过这个结构体可以大幅度简化 初始化(也可称之为向uSer的注册)函数 的 接口形参数量。
饭好了,先吃饭~~
点赞
2013-8-2 20:12
板凳
辛昕
新的uSCore模块
// in uSCore.h
#ifndef _US_CORE_
#define _US_CORE_
#include "typedef.h"
#include "CommonMacro.h"
typedef struct
{
void (*TimerIsrContainer)(void);
}uSCoreFunc;
void uSCoreFunc_Initial(uSCoreFunc *Str);
void uSCore_Initial(uSCoreFunc *pFuns);
#endif
#include "uSCore.h"
#include
#include "uSCoreTest.h"
uSCoreFunc uSCoreInit;
void uSCoreFunc_Initial(uSCoreFunc *Str)
{
Str->TimerIsrContainer = NULL;
}
void uSCore_Initial(uSCoreFunc *pFuns)
{
uSCoreFunc_Initial(&uSCoreInit);
if( (uSCoreInit.TimerIsrContainer != NULL) && (pFuns->TimerIsrContainer != NULL) )
pFuns->TimerIsrContainer = uSCoreInit.TimerIsrContainer;
}
// end of file -----------------------------------------------------------------
这里要注意一个 结构体初始化 的函数。
两点说明:
1.之所以以结构体指针传递要初始化的结构体,因为在Apper必然要使用到这个类型的结构体来传递函数指针。
因此,它的初始化不只是在这里要用大;
2.其二
为什么要首先初始化NULL。初始化为NULL或者变量初始化为0
,很多人也许只是一个良好习惯而不知道为什么。
这里理由很简单,你需要任何时候让变量处在一个确定的状态,否则,特别是 状态类变量,总会有让你意外的事情发生,理由也很简单,因为你没有初始化他,它处在一个未定状态。自然程序也会有不确定走向。
另外就是,我们前面说过,
在执行一个函数指针以前,必须要判断它是否非NULL,否则万一执行了一个NULL指针,谁也不知道程序到底会跑到什么地方去。
在我的stm8s105系统里,它是
进入一种类似死机的状态
。
[
本帖最后由 辛昕 于 2013-8-2 20:58 编辑
]
点赞
2013-8-2 20:46
4楼
辛昕
回到uS_App.考虑如何调用
在uSCoreFunc里包含了(目前只有它,因为我们只需要它)
包含了我们要放到 定时中断里的函数。
因此,我们在Apper里,首先要找一个合适的地方,定义这样一个结构体指针。
最终,综合考虑一下,我放弃了原来的简单 把初始化放在main()函数前,而是另外设置一个uSInit作为独立的初始化模块——当然,它不仅限于只初始化uSCore部分,以后的所有uS初始化都将放在这里,否则这个模块就划分的太不科学了,太繁琐了。
#include "uSInit.h"
#include "uSCore.h"
#include
uSCoreFunc uSCoreReg;
void uSCore_Pre(void)
{
uSCoreFunc_Initial(&uSCoreReg);
}
void uS_Init(void)
{
uSCore_Pre();
uSCore_Initial(&uSCoreReg);
}
我们将注意到。
这个初始化模块,包含了一个非常重要的结构体, uSCore_Reg。
因为它,它将被大多数模块调用。
因此,考虑到这个关系。
我决定给这个模块换个名字。
叫什么好呢?
.........................
想了好一会都没想到,算了,先押下。
毕竟这个历程是软件的生命之旅,所以任何时候都可以改变。
这是这个 软件隐喻 最温暖人心的地方。
点赞
2013-8-2 21:13
5楼
辛昕
终于回到我们的正题,把LED闪烁函数加入定时器中断,测试uSCore...
这个帖子的时间间隔稍微长了一点。
因为我现在继续写这个帖子的时候,我已经完成了 标题里写的内容了。
几乎零点了。
之所以慢,是因为真切遇到一些需要考虑如何实现更好的方法。
几乎可以说,这是第一次实质性遇到的麻烦,也将决定了将来这个uSer的实现基本思路。
我决定先贴出部分代码,然后洗完澡,接着或者明天才来具体解释。
代码从下一个帖子开始贴
点赞
2013-8-2 23:42
6楼
辛昕
当前的 uSer
当前uSer只有两个源文件
头文件基本只是函数声明,所以除非我新增加了什么重要的宏或者结构体定义,否则就不再贴出头文件了。
#include "uSCore.h"
#include
#include "uSCoreTest.h"
#include "uSTask.h"
uSCoreFunc uSCoreInit;
uSCoreFunc *get_uSCoreInit(void)
{
return (&uSCoreInit);
}
void uSCoreFunc_Initial(uSCoreFunc *Str)
{
Str->TimerIsrContainer = NULL;
Str->Led_4_Test = NULL;
}
void uSCore_Initial(uSCoreFunc *pFuns)
{
uSCoreFunc_Initial(&uSCoreInit);
if(pFuns->Led_4_Test != NULL)
uSCoreInit.Led_4_Test = pFuns->Led_4_Test;
uSCoreInit.TimerIsrContainer = uS_TaskList;
if(uSCoreInit.TimerIsrContainer != NULL)
pFuns->TimerIsrContainer = uSCoreInit.TimerIsrContainer;
}
// end of file -----------------------------------------------------------------
#include "uSTask.h"
#include "uSCore.h"
void uS_TaskList(void)
{
static U16 Times = 0;
Times++;
if(Times & 0x100)
(*(get_uSCoreInit()->Led_4_Test))();
}
这里先简单介绍一下这个uSTask的设置原因。
因为,整个uSer的各个部件,都有可能往这个 将放在定时器中断服务程序 里的函数,这个时候我们不妨称之为 Task也就是任务了。
因此,它应该独立出来,高于其他已知设定的两三个独立逻辑部块。
[
本帖最后由 辛昕 于 2013-8-2 23:51 编辑
]
点赞
2013-8-2 23:46
7楼
辛昕
当前的Apper 在我的项目里,我命名为 uS_App,作为开发uS过程中的测试项目
包含4个源文件
第一个自然是主源文件main函数所在
#include "uSInit.h"
#include "uCGpio.h"
#include "uCTimer.h"
void SystemInitial(void)
{
uSReset();
uCGpio_Initial();
}
void main(void)
{
SystemInitial();
uS_Init();
Timer_Initial();
while(1);
}
这里必须说明,因为这个时候,有一个顺序的问题要处理(跟通过函数指针和uSer进行“进进出出”的函数交换所致),所以,目前还是暂时调出预期效果的结果。
接下来的整理和重构,第一个要对付的就是这个地方,一定要努力使他与顺序无关或者明确一个好的顺序,或者让这个顺序不易混淆的措施。
一定要让使用者容易使用为己任。
接着是新增加的uSInit模块,它是给Apper集中初始化uS各部件的。
#include "uSInit.h"
#include "uSCore.h"
#include
uSCoreFunc uSCoreReg;
void uSReset(void)
{
uSCoreFunc_Initial(&uSCoreReg);
}
void uS_Init(void)
{
uSCore_Initial(&uSCoreReg);
}
uSCoreFunc *get_uSCoreReg(void)
{
return (&uSCoreReg);
}
然后是 定时器中断 和 GPIO的底层设置
#include "uCTimer.h"
#include "stm8s_tim2.h"
#include "uSInit.h"
#include
void (*TimerIsr)(void) = NULL;
void Timer_Initial(void)
{
TIM2_TimeBaseInit( TIM2_PRESCALER_16 ,400);
TIM2_PrescalerConfig(TIM2_PRESCALER_16,TIM2_PSCRELOADMODE_IMMEDIATE);
TIM2_ARRPreloadConfig(ENABLE);
TIM2_ITConfig(TIM2_IT_UPDATE , ENABLE);
TIM2_Cmd(ENABLE);
if(get_uSCoreReg()->TimerIsrContainer != NULL)
TimerIsr = get_uSCoreReg()->TimerIsrContainer;
__enable_interrupt();
}
#pragma vector=0xF
__interrupt void TIM2_UPD_OVF_BRK_IRQHandler(void)
{
TIM2_ClearITPendingBit(TIM2_IT_UPDATE);
if(TimerIsr != NULL)
(*TimerIsr)();
}
#include "uCGpio.h"
#include "stm8s_gpio.h"
#include "uSInit.h"
#include
void (*Led_4_Test)(void) = NULL;
void uCGpio_Initial(void)
{
GPIOD->DDR |= 1<<0;
GPIOD->CR1 |= 1<<0;
get_uSCoreReg()->Led_4_Test = uCGpio_Toggle;
}
void uCGpio_Toggle(void)
{
static U8 Status = 0;
Status = !Status;
if(Status == 0)
GPIOD->ODR |= 1<<0;
else
GPIOD->ODR &= ~(1<<0);
}
[
本帖最后由 辛昕 于 2013-8-2 23:52 编辑
]
点赞
2013-8-2 23:50
8楼
kobe1941
楼主头像戴着红领巾,,顶一个
点赞
2013-8-4 19:44
9楼
辛昕
回复 8楼kobe1941 的帖子
那是 红领带.......
虽然差了一个字,,,,红领巾是我十来岁的时候带的
带红领带的时候我快24了~~~大学毕业照
点赞
2013-8-4 21:18
10楼
木瓜子
回复 板凳辛昕 的帖子
void uSCore_Initial(uSCoreFunc *pFuns)
{
uSCoreFunc_Initial(&uSCoreInit);
if( (uSCoreInit.TimerIsrContainer != NULL) && (pFuns->TimerIsrContainer != NULL) )
pFuns->TimerIsrContainer = uSCoreInit.TimerIsrContainer;
}
不知道这里的if 语句还有什么作用,第一行就已经
uSCoreInit.TimerIsrContainer = NULL,
那if根本就没起作用啊,不理解啊不理解
话说开头看就优点被绕晕的感觉,菜鸟就是这样了,什么时候才能得到这高度啊
点赞
2013-8-27 21:30
11楼
辛昕
回复 10楼木瓜子 的帖子
是的。
这个地方写错了。
我刚检查了我最新的代码里,因为改动比较大(名字,甚至是后来的 注册方式 都变化了很多)
但从我对比和回忆。
我当时的确是这个地方写错了。
我记得我后来好像改成 只检查 参数的成员 非NULL。
点赞
2013-8-27 23:22
12楼
木瓜子
回复 6楼辛昕 的帖子
typedef struct
{
void (*TimerIsrContainer)(void);
}uSCoreFunc;
这是定义的结构体,里面只一个函数指针成员,但我不知道下面的这个函数,其参数怎么会出现了另一个成员Led_4_Test ?
void uSCoreFunc_Initial(uSCoreFunc *Str)
{
Str->TimerIsrContainer = NULL;
Str->Led_4_Test = NULL;
}
话说怎会这么少人关注啊,我是才开始发现版主发的这个系列us成长,就关注起来,俺一个菜鸟,多学习一些前辈高手的编程思想
[
本帖最后由 木瓜子 于 2013-8-28 23:11 编辑
]
点赞
2013-8-28 23:07
13楼
辛昕
回复 12楼木瓜子 的帖子
well
是这样的。
我这是直播的内容,所以前后改动比较大。
你现在弄的是 定时器那一部分。
我建议你去 第六 还是第七篇,找 uS-v0.1释出版。
那是完整的而又纯粹的只有定时器 的 最终版。
当然你也可以找到我现在醉心释出的 uS v0.2
但我记得,这个部分我做过的改动并不多,只是改了个名字。
这里跟你道个歉。
因为理论上来说,我不应该随便改变接口或者名字。
但是因为这个东西是从无到有,我一开始很难做到一成不变的设计。
所以整个过程都是很迅猛变化的。
我的建议是
你找到两个 释出版本
uS v0.1 和 uS v0.2
然后分别往回看,这样不至于出现太多奇怪的地方。
点赞
2013-8-28 23:13
14楼
辛昕
回复 12楼木瓜子 的帖子
嗯,多谢你的夸奖。
我喜欢你提到的 成长。
是的,这也是我这个系列直播贴的初衷。
另外感谢你作为第一个真的在看我做的整个过程的人。
对于前面说的,我加一个补充。
你在看的过程中会遇到一些你觉得奇怪的地方,这是正常的。
正如前面所说,这是一个成长的过程。加上我写程序的时候,并不急于保证每一步做的都是可以马上编译运行——而是一个设计过程和思路的完整展现。
——事实上我觉得这样的编码方式更健康,更容易进行——这叫增量式开发过程。
所以,我给你的建议是,你遇到看不懂不理解的地方,可以先记下,然后接着往下看。
我每一次都只完成一个独立模块,然后分别以 uSv0.1 uSv0.2 冠名。
因此你每次也不需要看的太远,只要看到当前的 模块释出 就可以 回头整理 所遇到的问题了。
——我相信,在我的直播贴里,你会看到所有变化的源头和原因。
然后,非常欢迎你经常这样反馈意见。
因为对于你的回复,我非常感动!
点赞
2013-8-28 23:19
15楼
木瓜子
回复 11楼辛昕 的帖子
还有不对劲的啊, 我怎么觉得是应该第一句去掉的,看着这个函数的本意似乎是,如果被传递的函数指针不为空,且参数所指的函数指针也不为空,就将被传递的函数指针赋值给参数所指的指针
void uSCore_Initial(uSCoreFunc *pFuns)
{
uSCoreFunc_Initial(&uSCoreInit);
if( (uSCoreInit.TimerIsrContainer != NULL) && (pFuns->TimerIsrContainer != NULL) )
pFuns->TimerIsrContainer = uSCoreInit.TimerIsrContainer;
}
也就是应该是
void uSCore_Initial(uSCoreFunc *pFuns)
{
if( (uSCoreInit.TimerIsrContainer != NULL) && (pFuns->TimerIsrContainer != NULL) )
pFuns->TimerIsrContainer = uSCoreInit.TimerIsrContainer;
}
不知道我说的对不对
点赞
2013-8-28 23:21
16楼
木瓜子
好,我找下你说的那两个版本,仔细看下,我看的时候是一点一点仔细看,想明白了才往下看,所以总是被卡住
点赞
2013-8-28 23:27
17楼
辛昕
回复 16楼木瓜子 的帖子
嗯,先从每个释出版的地方获得当时的最后版本,再回去看就好了。
另外,我强烈建议你,在你使用的单片机上试用一下这个程序。
我有一个uS_App,你只需要根据你自己的程序,修改一下相关的寄存器设置就可以了。
点赞
2013-8-28 23:30
18楼
木瓜子
回复 17楼辛昕 的帖子
我会试试看的,谢谢斑竹的指教
点赞
2013-8-28 23:35
最新活动
报名直播赢【双肩包、京东卡、水杯】| 高可靠性IGBT的新选择——安世半导体650V IGBT
30套RV1106 Linux开发板(带摄像头),邀您动手挑战边缘AI~
安世半导体理想二极管与负载开关,保障物联网应用的稳健高效运行
免费申请 | 上百份MPS MIE模块,免费试用还有礼!
PI 电源小课堂|无 DC-DC 变换实现多路高精度输出反激电源
2024 瑞萨电子MCU/MPU工业技术研讨会——深圳、上海站, 火热报名中
随便看看
ccs3.3编译问题
全国大学生电子设计竞赛模板
印刷机的控制(z)
CubeSuite+进阶使用
【EEWORLD第二十四届】2011年03月社区明星人物揭晓!
POS机一般用哪家的ARM芯片!
各位大侠帮哈忙
油价又涨了......奔7了
在vs 2005 IDE下开发opengles的智能设备工程
SPI总线操作完成的确认
分享MSP430单片机---Timer_A输出PWM
STEVAL-IDB007V1板子的烧录问题
为什么要使用仿真器
数字及模拟电路仿真软件
关与输入法问题
纸机在线 内嵌式测量系统的应用%
【求助】uart问题
推荐大家购买的图书
SD卡读写速度问题。
能用查询调制解调器得到at返回信息,却不能用串口工具得到
电子工程世界版权所有
京B2-20211791
京ICP备10001474号-1
京公网安备 11010802033920号
回复
写回复
收藏
回复