搜索
bottom↓
12
返回列表 发新帖
楼主: yrloy

【原创】MCU程序设计高手进阶(连载)

  [复制链接]

出0入0汤圆

发表于 2013-6-28 19:54:10 | 显示全部楼层
不错             楼主继续啊。。。     啥时候出第三题?

出0入0汤圆

 楼主| 发表于 2013-6-28 20:11:25 | 显示全部楼层
leijiayou 发表于 2013-6-28 19:54
不错             楼主继续啊。。。     啥时候出第三题?

感谢支持!
原定每周一题,本周超了,先要休息下。
本帖的意义是大家讨论,要解答或和大家探讨太费体力了。

出0入0汤圆

发表于 2013-6-28 20:59:53 | 显示全部楼层
yrloy 发表于 2013-6-26 21:22
立刻有朋友在没时间测试的情况下就答出正解,真是好感觉。
感谢参与讨论的人,这会使大家一起进步 ...

      老师,原谅我还在说第一题,我可否将您这里所说的原子操作理解成:读取一次完全更新过的数据,也就是不能读取被打断、被其他地方改变的数据。这里您只是要求读取的数据的完整性,却无法保证数据的精确性,比如我在等待一个时刻采取某个动作,但是当这一刻来临时,比如为10,我在读取的过程中,中断发生了,要对该变量加1操作,因此我此次读取的数据便是无效了,只能再次读取,但是更新过后的数据却是11了,也就是我错过了10的那一刻,这样以后我可能永远都无法执行那个动作了。
      但是关中断却可以避免该变量被中断或者其他的系统任务修改。
     不知道我的理解对不对,请老师指正。

出0入0汤圆

 楼主| 发表于 2013-6-28 22:29:23 | 显示全部楼层
chenxujiaoyang 发表于 2013-6-28 20:59
老师,原谅我还在说第一题,我可否将您这里所说的原子操作理解成:读取一次完全更新过的数据,也就 ...

什么老师,大家一起讨论而已,可别客气。

这段读代码多说十几个周期,说明离时钟切换已经很近,中断后已经是新的时间了。
怎么会错过了就无法实现那?

看下面这个函数,放在主循环里,定时执行某些动作,定时周期为10个全局时钟单位:

void task(void)
{
    static unsigned long delay = 0;    /* 用于延时 */
    static int index = 0;                   /* 状态索引 */

    if (index == 1) {
        if (get_jiffies() >= delay) {
            /* 执行动作... */
        }
        index = 0; /* 恢复初始状态 */
     }

    if (index == 0) {
        delay = get_jiffies() + 10;    /* 记录10T之后时刻 */
        ++index;
    }
}

简化的状态机,不会阻塞主流程的。

出0入0汤圆

发表于 2013-6-29 01:25:33 | 显示全部楼层
yrloy 发表于 2013-6-28 19:12
第二题做个了结吧,让大家久等了,这不是标准答案,大家完全可以有自己的理解

ANSI C虽然规定了变量的最小 ...

从《最高效率使用单片机。。。》那个帖子转过来的, 惊为天人!楼主是少见的水平高又肯详细讨论的人,赞一个先

惭愧,第一个问题不会,没深入考虑过。看了答案,受教。

第二个问题仍然不得其解,继续看答案,不过这次没看懂,想探讨一下。

1,先说个细节问题, 楼主说短类型并不一定快,还是有很多情况下快的,养成倾向于用短类型的习惯,还是好处大于坏处吧,尤其是如果涉及到大量的重复,比如像素操作,无论每次循环快几个机器周期还是每个像素少用几bit空间都是有意义的,而代价并不需要你花精力做深入分析,只要养成用短类型的习惯
2,题目主体:感觉楼主的答案并没回答清楚问题 ---- 为什么从大量使用typedef 退回到使用 ANSI 原生类型?楼主的意思是不是:这么多情况下,原生类型都很简单很明确,再用typedef属于画蛇添足?

说点个人理解,仅供探讨。C最开始出现的时候,特点之一就是“中间语言“,意思是介于高级预言(BASIC)和低级语言(汇编之类)之间的一种语言。这么说并非仅仅是噱头,而是有重要原因的。其中之一就是其类型定义,C最早制定这些类型的依据就是希望各种处理器可以方便地处理C的各种类型,也就是说,贴近硬件。比如8位机可以单条汇编指令实现char型加减甚至乘运算,再比如8086系列硬件就支持符号数和无符号数,恰好对应int 和unsigned int 。这样,用原生定义在运算优化方面就一目了然,可以让一般工程师在程序设计的层面,对算法进行与处理器架构有关的优化,这样的层次,比BASIC之类厉害多了。另一方面,如果使用u8/u16之类的定义,可以使数据宽度一目了然,在很多场合可以带来很大方便,比如USART,就是8位(7位你不也得用8位么,总之不能用16位),这时使用u8就带来移植方面的优势。

总之,我觉得原生类型利于特定平台的,同时对计算优化更有利,u8/u16这样的定义更利于跨平台的,同时倾向于存储什么的。因此各有利弊,所以linux有取有舍,而一窝蜂大量使用并不是太好。不过,额,,,stm32的库似乎是一窝蜂大量使用的,转的头晕,可是他还是不肯改,所以心里私下还是有点犯嘀咕

出0入0汤圆

发表于 2013-6-29 09:59:11 | 显示全部楼层
昨天浏览了大作,真心赞一个!
希望更多的人来进行讨论。
不过昨天太晚,哥就没参加回复了。

出0入0汤圆

发表于 2013-6-29 10:02:24 | 显示全部楼层
Louis_Bright 发表于 2013-6-29 09:59
昨天浏览了大作,真心赞一个!
希望更多的人来进行讨论。
不过昨天太晚,哥就没参加回复了。 ...

第一个问题,我的答案和4楼一样,看样子是楼主想的答案,小小得意了一把。
倒是第二个问题(恕我直言,楼主问题本身并没有描述得太清楚,也就是别人不太好把握你的“中心思想”),引起我的浓厚兴趣。
我将自己的一些体会也写出来,大家一起讨论。

出0入0汤圆

发表于 2013-6-29 10:18:15 | 显示全部楼层
我们首先讨论函数的局部变量。个人认为,在没有特殊的情况下,局部变量最好使用“本位宽度”,也就是说,如果是8位机,局部变量就用8位;16位机局部变量就用16位,32位机局部变量就用32位——有特殊需求使用其他位宽又零担别论。
这个原因就在于单片机对本位宽度操作是最有效的支持,往往会体现速度最快,甚至内存最少,也更容易优化。
老实说,这是我十多年写下位机的体会。
在这个认识的过程中,也确实会有当时提到的“能用短类型就用短类型”的认知,这其实是当时8位机,尤其是51单片机的横行引起的后遗症,如果把“能用短类型就用短类型“放到8位机中,无疑是正确的。但会存在移植性不好的问题,而且在高位宽的单片机上跑可能需要更多的时间和空间。
这时有人可能还会反驳这个观点,说假如在32位单片机中,局部变量明明范围不会超过一个byte,你还用4个byte,不会要多一些内存吗?其实这个楼主有解释过,就是这些局部变量会存在于“堆”或者“栈”中,整体下来,既不会增加内存,也不会影响速度的。

出0入0汤圆

 楼主| 发表于 2013-6-29 10:18:30 | 显示全部楼层
食肉动物 发表于 2013-6-29 01:25
从《最高效率使用单片机。。。》那个帖子转过来的, 惊为天人!楼主是少见的水平高又肯详细讨论的人,赞 ...


您好,真的不敢当,感谢支持!

您说的历史背景非常好,说明了根本原因:不具体规定类型长度,恰恰是想各平台可以更好的提高运算效率!

主要浪费时间的是算数运算,但按照C标准,算数运算时,首先有如下类型转换规则:
1、char、short型转换为int型再参与运算。
2、float型数据在运算时一律转换为双精度(double)型,以提高运算精度。

这是恰恰是因为只要是在16\32\64所有架构下,char与int速度一样(有些操作int更快),int显然更安全,比定长更适合移植。
而8位架构,8位确实比其它运算快,预想中应该出意外,但出乎人意料,编译器也是符合标准的的计算转化方式。
但是都不是问题,无论何种架构,编译器的优化都很强大,不是说多了几条指令,而是大多数情况都完全一样,是该架构最快的运算方式。
这样可读性、安全性、移植性,在跨平台编程中价值更高。

再说说你说的存储问题,其实并不存在,栈上内存和寄存器不占用实际内存。而全局定义的数据表如101楼第5条已说明。

当然,如您所说,不能极端,所以101楼、80楼,列举很多该使用的情况。
这本就是开放性思考,没有答案,谢谢你的真心投入思考,这样大家、你和我都能真正受益。

出0入0汤圆

发表于 2013-6-29 10:19:59 | 显示全部楼层
我还有其它一些观点,但很多观点都会基于“局部变量尽量使用单片机的本位宽度”,所以暂且搁置,等大家肯定或者否定我的这个观点再说。

出0入0汤圆

 楼主| 发表于 2013-6-29 10:29:59 | 显示全部楼层
本帖最后由 yrloy 于 2013-6-29 10:31 编辑
Louis_Bright 发表于 2013-6-29 10:18
我们首先讨论函数的局部变量。个人认为,在没有特殊的情况下,局部变量最好使用“本位宽度”,也就是说,如 ...


谢谢您的支持,您功力深厚,实战中得来的经验
但我确有些观点不同

先说8位机的问题(16\32\64完全不存在问题),51先不说,它上面的变量反汇编一看局部变量全是静态地址,这是历史遗留问题,不会再有了。
现代8位单片机编译器处理方式,传参和局部变量甚至连栈都不用(除非参数、变量爆多,那是程序结构有问题),全部都在寄存器中,最快更不会占用内存。

局部变量,如果显式的少于一定宽度,编译器自然处理,如下:

    int i;
    for (i = 0; i < 100; ++i) {
        /* 代码 */
    }

编译器不会使用有符号数,更不会比8位变量多一条指令,也不会多用一个字节。


谢谢您,有点事,有话没说完,晚些跟您讨论。

出0入0汤圆

发表于 2013-6-29 10:35:00 | 显示全部楼层
yrloy 发表于 2013-6-29 10:29
谢谢您的支持,您功力深厚,实战中得来的经验
但我确有些观点不同

这就是我和你在理解上的一个很大的区别。
我承认现在很多编译器会自动处理,
但是,我们需要“承前启后”,还有很多旧的东西我们需要考虑兼容。

出0入0汤圆

发表于 2013-6-29 11:24:25 | 显示全部楼层
没有搞懂,哈哈。
我一般都是
typedef unsigned char u8;
typedef unsigned int u16;
很少用char int之类的。难道又被误导了?谢谢楼主的无私奉献。

出0入0汤圆

发表于 2013-6-30 09:53:50 | 显示全部楼层
yrloy 发表于 2013-6-27 10:47
你好,这个地方要看个人理解,很多人赞成你的观点,也有很多反对(我也是)。

我最初写代码很极端,计较 ...

楼主 请教一下
你在帖子中提到接口的概念,在下有点模糊..

我说说我的理解,你看看对不
假设,某个函数中需要用到__jiffies,
1.
  1.    
  2.      unsigned long tmp;
  3.      do {
  4.         tmp = __jiffies;
  5.     } while(tmp != __jiffies);
  6.     if(temp > ABC)
  7.     .......
复制代码
2.
  1.    
  2.     if(get_jiffies(void)> ABC)
  3.     .......
复制代码
2这种方式就是你所说的接口么?
排除可重复性这一优点,请问2这种方式还有其他什么优点么?

出100入0汤圆

发表于 2013-6-30 10:00:32 | 显示全部楼层
mark                              

出0入0汤圆

发表于 2013-6-30 11:00:48 | 显示全部楼层
yrloy 发表于 2013-6-28 19:12
第二题做个了结吧,让大家久等了,这不是标准答案,大家完全可以有自己的理解

ANSI C虽然规定了变量的最小 ...

根据那两个条件得出的五个结论之前确实没有好好思考过..
但是我觉得一套程序中还是统一为好,既然已经使用了<stdint.h>,也就没有必要把8位变量用unsigned char去定义,这个时候用uint8_t去定义,这个程序定义的风格就更加统一。

另外建议楼主开一个系列,每个问题开一个帖子,这样讨论起来会更有针对性一些,否则问题多了就愈发杂乱了。

出0入0汤圆

 楼主| 发表于 2013-6-30 17:40:11 | 显示全部楼层
277955973 发表于 2013-6-30 09:53
楼主 请教一下
你在帖子中提到接口的概念,在下有点模糊..

接口引用好处很多(当然接口的意思比较宽泛,不只是例子,你可以意会一下

一类算法或独立操作都放同一组源文件中,内部的全局变量必须显示禁止外部调用(全部声明为static)

这样最大限度的降低的文件之间的耦合,单位文件可以单独进行输入输出的测试
而且接口一致,内部的实现改变不影响整个工程。
又由于完全是黑盒,使用者无需知道内部原理(禁止修改内部,避免很多问题),只需学会头文件的接口即可。

不用担心效率问题,紧密的计算都在同意源文件中(直接引用),外部使用的函数接口都很简单。
越大的工程这样做的好处越多。

以上这些是我对接口引用一部分体会,有些我实在说不清。
你可以多看看软件工程的书,PC上编程,不分何种语言,这是基本原则。

出0入0汤圆

发表于 2013-6-30 21:23:38 来自手机 | 显示全部楼层
mark:........................

出0入0汤圆

发表于 2013-7-1 01:14:01 | 显示全部楼层
MARK , 好东西!!

出0入0汤圆

发表于 2013-7-1 09:51:48 | 显示全部楼层
好贴 先mark

出0入0汤圆

发表于 2013-7-2 06:44:05 | 显示全部楼层
楼主继续,我慢慢细读,最近也开始关注起来这些地方

出0入0汤圆

发表于 2013-7-2 07:06:29 | 显示全部楼层
如何完成一个无需开关中断的原子读操作,保证读__jiffies变量的原子性?


Use a flag to indicate the write operation on the variable. That flag is typically set before the variable is updated in the isr and cleared after the variable is updated.

出0入0汤圆

发表于 2013-7-2 07:08:30 | 显示全部楼层
2、认为在中断函数建立数据拷贝

这个理由同上,无论如何复制,都难以避免读的瞬间数据被破坏


You can make that work too, assuming the isr is not re-entrant.

出0入0汤圆

发表于 2013-7-2 08:43:24 | 显示全部楼层
菜鸟来学习下。。。

出0入0汤圆

发表于 2013-7-2 19:41:57 | 显示全部楼层

好像很实用的样子

出0入0汤圆

发表于 2013-7-2 23:24:27 | 显示全部楼层

出0入0汤圆

发表于 2013-7-4 11:29:13 | 显示全部楼层
mark,收藏下。

出0入0汤圆

发表于 2013-7-4 22:56:02 来自手机 | 显示全部楼层
goodcode 发表于 2013-6-27 00:57 一直搞windows下的开发, 玩单片机也有日子了还真没去注意同步这个事... 好在做的一些小玩意没出问 ...

正解,想必你这样一举例会给很多看不懂楼主题目意思的家伙来个恍然大悟的感觉

出0入0汤圆

发表于 2013-7-5 00:34:09 | 显示全部楼层
登记个慢慢理解消化,有个疑问就是所有的编译器都会默认吧char short转为int 进行运算么(同理单精度转双精度)?那么对于51一类的单片机不是就有些吃力了么?

出0入0汤圆

发表于 2013-7-5 01:45:09 | 显示全部楼层
占个位学习      

出0入0汤圆

发表于 2013-7-5 13:46:37 | 显示全部楼层
学到不少东西。谢谢楼主分享。

出0入0汤圆

发表于 2013-7-5 16:10:40 | 显示全部楼层
学习了 好贴~

出0入0汤圆

发表于 2013-7-15 16:14:19 | 显示全部楼层
yrloy 发表于 2013-6-28 19:12
第二题做个了结吧,让大家久等了,这不是标准答案,大家完全可以有自己的理解

ANSI C虽然规定了变量的最小 ...

谢谢楼主的分享!
第一题很有启发,第二题我有些看不懂,比如:
4、编写操作外部设备的驱动或通信协议包时,应该包含<stdint.h>头文件,使用里面定义的标准类型
这句话没看懂,比如为什么要用uint8_t,而不是直接用unsigned char呢?

出0入0汤圆

发表于 2013-7-17 12:41:58 | 显示全部楼层
楼主..怎么没有继续下去了呢..一直在等着呢

出0入0汤圆

发表于 2013-8-23 10:17:46 | 显示全部楼层
哎..怎么都没什么人关注呢..楼主?哪去啦

出0入0汤圆

发表于 2013-8-27 12:46:33 | 显示全部楼层
果然是高手,顶。

出0入0汤圆

发表于 2013-8-27 13:36:11 | 显示全部楼层
是我太菜吗,根本不知道你们说的原子锁是什么东东

出0入0汤圆

 楼主| 发表于 2013-8-28 15:49:15 | 显示全部楼层
277955973 发表于 2013-8-23 10:17
哎..怎么都没什么人关注呢..楼主?哪去啦

楼主换工作了,开始还比较晕

过一两个月,我一定重新开帖,发布新的问题,真心感谢您的支持

出0入0汤圆

发表于 2013-8-29 13:32:17 | 显示全部楼层
yrloy 发表于 2013-8-28 15:49
楼主换工作了,开始还比较晕

过一两个月,我一定重新开帖,发布新的问题,真心感谢您的支持{: ...

嘿嘿...我会监督你的

出0入0汤圆

发表于 2013-8-30 18:12:47 | 显示全部楼层
支持楼主!

出0入0汤圆

发表于 2013-9-1 07:35:35 | 显示全部楼层
写的不错

出20入186汤圆

发表于 2013-10-11 18:21:11 | 显示全部楼层
zishan 发表于 2013-6-26 20:19
个人见解,读操作没必要关中断.

楼主说了,要跨平台,8位,16位,32位,
不同平台上,读一个变量所需指令是不一样的,
如果你在读一个变量低字节时,刚好这个中断产生了,且更改了数据,
中断执行完返回 读变量高字节时,这时一个变量与实际变量就不一样了,所以要关中断,

出20入186汤圆

发表于 2013-10-11 19:22:06 | 显示全部楼层
楼主好贴,
学习了,
特别是1楼的问题
之前遇到过,就是让步进走动指令距离,
后来发现运行时,会偶然出现没到距离伺服就停了
后来调试发现,停的时候,两个值并没有相等
我程序中要检查 当前位置和目标位置是否相等,如果相等则停
有很小的机率会出现,目标位置没到,但是if(当前位置==目标位置)的后边的内容却执行了,
我追踪到IF内的语句时,却发现监视里两个值并不相等,
后来我才注意到 临界区 这个概念,因为我是在51里边写的程序,定义的32位的,
一个++操作是有多个指令的,而在读这个操作时,中断触 发了,并且++了变量
而我判断时就出问题了
后来解决办法是:

将判断是否相等的语句放到中断里去执行
这样就解决了

不过这种方法只能特殊情况特殊对待,不通用,
http://www.amobbs.com/thread-5381979-1-1.html
这个是我之前出现的问题,呵,差不多2年了,当时还很幼稚,

再后来換成lpc1114,32位的,也不会出现这种问题,变量直接32位

看了楼主的方法,豁然开朗,以后不管它变量是什么样的,不要求变量刚好和系统位数相等,
只要用了楼主这方法,管它多少位系统,统统都能解决,

出0入0汤圆

发表于 2013-10-11 20:14:31 | 显示全部楼层
讲得非常好,感谢LZ,收获不少,希望继续连载。。。

出0入0汤圆

 楼主| 发表于 2013-10-12 12:37:44 | 显示全部楼层
yuyu87 发表于 2013-10-11 19:22
楼主好贴,
学习了,
特别是1楼的问题

谢谢支持,我们共同讨论,彼此学习

出0入0汤圆

发表于 2013-10-12 13:10:30 | 显示全部楼层
果然是高手啊!!!

出0入0汤圆

发表于 2013-10-12 13:53:09 | 显示全部楼层
第一题帮助很大!!
有了很大的启发,谢谢。

出0入0汤圆

发表于 2013-10-12 14:04:26 | 显示全部楼层
受教了,,,

出0入0汤圆

发表于 2013-10-31 23:48:07 来自手机 | 显示全部楼层
linux内核就是这种方法读取时钟。。。。。

出0入0汤圆

发表于 2013-11-1 06:28:53 | 显示全部楼层
关于局部变量不占实际内存,我有些疑问. 如一个函数:
void func1(void)
{
    char abc;  //更改成long abc
}
的话,build后,map文件的Section size会有变化. 用的MCU的编译器显示,program section变大了几个字节.
这是怎么回事呢

出0入0汤圆

发表于 2013-11-1 16:48:19 | 显示全部楼层
学习一下,谢谢分享!

出0入0汤圆

发表于 2013-11-1 17:11:30 | 显示全部楼层
好深奥啊。。。只能看懂一些。。菜鸟来学习学习

出0入0汤圆

发表于 2013-11-2 00:59:38 | 显示全部楼层
受益匪浅啊!暂时没有什么独立的见解。表示要好好学习,学好了再参加讨论。

出0入0汤圆

发表于 2013-11-2 07:46:42 | 显示全部楼层
受教了,多谢

出0入0汤圆

发表于 2013-11-2 08:02:16 来自手机 | 显示全部楼层
占楼,学习!

出0入0汤圆

发表于 2013-11-2 11:25:16 | 显示全部楼层
yrloy 发表于 2013-6-26 21:22
立刻有朋友在没时间测试的情况下就答出正解,真是好感觉。
感谢参与讨论的人,这会使大家一起进步 ...

这个方法并不是完全可靠,理论上来讲是能够进入死循环的哦。那么是否要做超时检测啊,如果怎么做了那么程序又人为的增加了亢余量。

出0入0汤圆

 楼主| 发表于 2013-11-2 11:30:06 | 显示全部楼层
etdz 发表于 2013-11-2 11:25
这个方法并不是完全可靠,理论上来讲是能够进入死循环的哦。那么是否要做超时检测啊,如果怎么做了那么程 ...

您好,感谢支持

无论如何,不可能进入死循环,而且最多循环一次。
中断处理程序前后有出栈和入栈时间,还有处理时间,少说几十个、甚至上百周期。
所以哪怕是连续的中断,也不可能让这个程序进入死循环,这个循环通常也就十几个周期,所以显然不可能。

而且如此高频繁的中断,必然是程序设计有误,否则还要主函数干什么?全放在中断里多好

出0入0汤圆

发表于 2013-11-3 19:40:48 | 显示全部楼层
我最初写代码很极端,计较指令级的得失,务必把代码优化到极致。

我也是~

出0入36汤圆

发表于 2013-11-3 19:54:20 | 显示全部楼层
好贴!期待继续连载---

出0入0汤圆

发表于 2013-11-7 09:01:09 | 显示全部楼层
LZ功力深厚
头像被屏蔽

出0入0汤圆

发表于 2013-11-8 09:00:11 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
头像被屏蔽

出0入0汤圆

发表于 2013-11-8 09:03:16 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2013-11-8 09:58:23 | 显示全部楼层
很不错,学了一招

出0入0汤圆

发表于 2013-11-11 14:10:32 | 显示全部楼层
楼主给力!

出100入101汤圆

发表于 2013-11-13 20:51:33 | 显示全部楼层
讲得不错,LZ继续

出0入0汤圆

发表于 2013-12-17 10:40:25 | 显示全部楼层
我现在碰到一个问题,ADC连续转换 通过DMA传到 ADC_ConvertedValue; 不进中断
我每隔1ms读一次这个值,我担心我读ADC_ConvertedValue的时候,ADC_ConvertedValue会被DMA送过来的值改变,不知道我这样写可以不?
  1. if( SYSTICK_FLAG_1MS )
  2.         {
  3.                 do{
  4.                         adc_buff[0] = ADC_ConvertedValue[0];       
  5.                 }
  6.                 while (adc_buff[0] != ADC_ConvertedValue[0]);
  7.                
  8.         }
复制代码

出0入0汤圆

发表于 2013-12-17 13:02:17 | 显示全部楼层
······················

出0入0汤圆

发表于 2013-12-17 16:59:06 | 显示全部楼层
好帖!还没有看第二题,第一题给我相当的震撼!

我一直以为多核才会有原子操作! 不曾想到,单核也是有此要求的!

我一直以为任何单核cpu的数据读写都是以其位数为标准的,也就是说32bit cpu,不管是读或者是写,肯定是一个流水线完成;

但是看了lz的解释,那就是说现在的单核cpu 读写不具备原子操作了,那我大胆的假设,如果写是3级流水线完成,那么这样的

cpu还能用吗?

出0入0汤圆

 楼主| 发表于 2013-12-17 18:44:09 | 显示全部楼层
mikal 发表于 2013-12-17 16:59
好帖!还没有看第二题,第一题给我相当的震撼!

我一直以为多核才会有原子操作! 不曾想到,单核也是有此 ...

您好,谢谢支持!

但是你误会了,并不是您的意思
比如8位单片机,通常它只是对单字节变量读写是原子的,对32位变量操作当然不是了
此时就需要采用一些手段,或是关中断、或是加锁,当然,还有文中的方法

出0入4汤圆

发表于 2013-12-17 18:47:41 | 显示全部楼层
COOL 帖@!

出0入0汤圆

发表于 2013-12-17 20:22:53 | 显示全部楼层
本帖最后由 mikal 于 2013-12-17 20:29 编辑
yrloy 发表于 2013-12-17 18:44
您好,谢谢支持!

但是你误会了,并不是您的意思


ok ,我明白了。关键是跨平台!

但是
unsigned long get_jiffies(void)
{
    unsigned long tmp;

    do {
        tmp = __jiffies;
    } while(tmp != __jiffies);

    return tmp
}
函数是有问题的!
假如是8bit操作,那么如果tmp != __jiffies的比较动作已经做了一个byte的比较,这是中断来了,变化的正好是比较的bit,这个时候,
结果肯定是tmp == __jiffies;但实际上__jiffies肯定不等于tmp;
呵呵!

出0入0汤圆

发表于 2013-12-17 20:30:53 | 显示全部楼层
原子操作有一个基本原则: 关中断或者cpu保证,其他方法都是不可能!不然,就不叫原子操作!
当然,我觉得lz能提出这样一个问题,也是相当的有思想!

出0入0汤圆

 楼主| 发表于 2013-12-17 22:13:02 | 显示全部楼层
mikal 发表于 2013-12-17 20:22
ok ,我明白了。关键是跨平台!

但是

这个值不会出现中间错误状态,必然是前一次或后一次的值。
您自己仔细想想究竟结果如何?
听您所说,就知道你自己肯定能想明白。


从理论来讲这不叫原子操作,但是从实际角度,原子操作是用来完成一次不可分割的操作,不让中间状态存在而引发错误。
从后者来说,这个实现达到了一致的效果。

出0入0汤圆

发表于 2013-12-18 10:04:03 | 显示全部楼层
yrloy 发表于 2013-12-17 22:13
这个值不会出现中间错误状态,必然是前一次或后一次的值。
您自己仔细想想究竟结果如何?
听您所说,就知 ...

我所表述的就是:前一次值或者后一次值,那么也就是说这个函数起的作用也仅仅是把原来的误差缩小,但不能消除!

如果直接读取,就是在进位的时候出现大的误差,平时即使有误差,也和您的函数误差一样!当然,你这个肯定要好于直接读取!

以后tick读取,就用你这个函数! 降低误差!



出0入0汤圆

发表于 2013-12-18 10:24:54 | 显示全部楼层
yrloy 发表于 2013-12-17 22:13
这个值不会出现中间错误状态,必然是前一次或后一次的值。
您自己仔细想想究竟结果如何?
听您所说,就知 ...

我再回头看了,您说对中断通信很有好处,的确,如果一般人来做类似的通信系统,可能会有用,
但是通信队列的设计 通常都是双索引,为什么用双双索引,就是为了避免中断改写指针的风险!
如果读索引小于或者等于写索引,那么就有数据,这个时候,即使写索引有任何变化,都是值在变大,而不会
变小,所以没有任何漏洞!这双索引的开销小,故通信队列,都如此实现,我们常称为ring buffer!

出0入76汤圆

发表于 2013-12-18 22:05:04 来自手机 | 显示全部楼层
这个帖子写的不错,LZ功力已yie

出0入0汤圆

发表于 2013-12-18 22:21:44 | 显示全部楼层
先收藏了再仔细看

出0入0汤圆

发表于 2013-12-19 09:37:29 | 显示全部楼层
mark,,感谢,这样一起分享心得,共同成长,

出0入0汤圆

发表于 2013-12-19 11:05:16 | 显示全部楼层
学习了很多,感谢楼主。但是对第二题有个疑问:
我用keil C51 9.05(单片机就选用了AT89C51) 测试了楼主给的这2个例子  unsigned int i 和unsigned cha ;  没有关优化的情况下,编译运行后显示:data大小一致,code int型占的多,而且关键是执行时间int的多,而不是所说的一样,求解答。

出0入0汤圆

发表于 2014-7-7 13:45:09 | 显示全部楼层
学习学习                  

出0入0汤圆

发表于 2014-7-7 14:19:01 | 显示全部楼层
不明觉厉

出0入0汤圆

发表于 2014-7-22 12:00:53 | 显示全部楼层
本帖最后由 myxiaonia 于 2014-7-22 12:12 编辑
chenxujiaoyang 发表于 2013-6-28 20:59
老师,原谅我还在说第一题,我可否将您这里所说的原子操作理解成:读取一次完全更新过的数据,也就 ...


我同意这种说法,就是保证数据完整性,但是和真正的值确实可能存在一个值的误差,这是必然的,设计程序时务必清楚这一点。
使用这种方法读取,出现值相差1的可能最大可能也许是万分之一的概览,相差2以上估计就忽略不计了

当然最关键的是 这是最简便的“原子”读取某个值的做法,说到误差性,其实开关中断同样也无法避免,理解这一点就好了

出0入0汤圆

发表于 2014-7-22 12:05:43 | 显示全部楼层
本帖最后由 myxiaonia 于 2014-7-22 12:13 编辑
Johnwoo 发表于 2013-6-27 13:07
同意。
semaphore主要用于资源数量大于1的时候,例如表示有限的并行缓冲区大小之类,这里用一个mutex就够 ...


51确实存在这样的一个互斥进入指令tstbit,keil c51在intrinsic.c中提供了几个指令级的函数,这是其中一个。
tstbit相当于 if(bit) { bit =0,...},相当于清零和跳转是一个原子操作。它最适合作为一个互斥锁了,而且还是指令级的互斥锁

出0入0汤圆

发表于 2014-7-22 12:09:22 | 显示全部楼层
yrloy 发表于 2013-12-17 22:13
这个值不会出现中间错误状态,必然是前一次或后一次的值。
您自己仔细想想究竟结果如何?
听您所说,就知 ...

我纠正个小bug,读取的值肯定是要么比真正值小,要么相等,不可能出现前或者后这样两种情况吧

出0入0汤圆

发表于 2014-7-22 13:41:33 | 显示全部楼层
zishan 发表于 2013-6-26 20:19
个人见解,读操作没必要关中断.

对的 这种程序算法,就算过了,也要不影响程序的运行就好了.
这种算法,也不能要求定时精度很高.
要么在中断中判断计数情况.
贴子久远了...

出0入0汤圆

发表于 2014-9-26 16:54:00 | 显示全部楼层
向高手学习。

出0入0汤圆

发表于 2014-9-26 19:42:43 | 显示全部楼层
顶一个!

出0入0汤圆

发表于 2014-9-27 10:24:02 | 显示全部楼层
楼主继续啊...咋就这个死了呢???

出0入0汤圆

发表于 2014-9-27 11:30:19 | 显示全部楼层
看看牛人的表演

出0入0汤圆

发表于 2014-9-27 13:06:32 | 显示全部楼层
占位听课

出0入0汤圆

发表于 2014-9-27 14:49:24 | 显示全部楼层
果断收藏。               

出0入0汤圆

发表于 2014-9-27 14:56:01 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2014-11-1 09:42:56 | 显示全部楼层
楼主继续啊...

出0入0汤圆

发表于 2014-11-1 21:34:17 | 显示全部楼层
占座学习,近看各位高手发言!

出0入0汤圆

发表于 2014-11-1 21:59:14 | 显示全部楼层
看看,,,,

出0入0汤圆

发表于 2014-11-2 17:44:32 | 显示全部楼层
我刚入门  还不能考虑这个呢
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-19 06:30

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表