十万个为什么 - 之0001

1.这个函数值得存在吗?

void WriteEEPRom(uint8_t addr, uint8_t *value, size_t size)
{
    size_t endPos = addr;
    unsigned char *data = (unsigned char *)value;
    endPos += size;
    if(endPos>255) return;
    while(addr<endPos) DATAEE_WriteByte(addr++, *(data++));
}

解答:

  1. 这是一个写EEPROM的函数,厂商只提供了WriteByte,这个函数扩展到了WriteBuffer。这种写法在写上位机代码的人中非常常见。但是在单片机从业人员中,非常罕见。上位机程序员非常喜欢封装。把代码累得很高。跨越多个层级结构。但是单片机编程中,时间开销和空间开销都不容忽视。
  2. data<-value的赋值很奇怪。对吧?它是有原因的。涉及到C90或者C99的细节。最初这个函数的接口定义value的定义不是uint8_t *,而是void *,编译器似乎有警告或者error。它改写后,就变成了这个不应该存在的样子。
  3. 这个函数即使存在,分数不应该超过70分,就是刚及格。

2.为什么单片机的代码里夹杂着那么多的延迟语句?

                if(getch()!='B') break;
                cnts_us=TIMEOUT;
                while(!EUSART1_is_rx_ready()&&(cnts_us!=0)){__delay_us(1);  cnts_us--;}

解答:

usDelay(), msDelay(),大多数情况下是缺乏反馈信息下的必须等待。举一个例子可能就足以让你了解这个过程:

485是个半双工通讯模式,对吧?在某些情况下,通讯双方没有流控制的手段,只有两根线,完全是盲发。并且在某些场景,没有中断介入,需要主动去Pull数据。这个时候,读和写的时机,就变得异常关键。半双工通讯,对于自己的发送和接收,总能找到方法,在发送完毕之后(不是指压入数据到TXR就算发送完毕,要等待peripheral的Sent信号才行)。但是你还必须留出对方发送完毕后,调整自己到接收模式的时间。在收到一个信号后,无延迟,立即回复,通常是不得体的,你需要等待对方进入发送完毕的接收状态。

另一个例子,上电过程或者掉电过程中,通常电源会产生一些波动,这个时候就造成单片机必须在上电会,稍稍延迟一会儿,再工作。类似这样。

3.单片机使用Python或者其他脚本语言是不是很高端?

解答:

单片机的代码量是论字节的。我最初听到Python可以跑在单片机上,也觉得诧异,后来,意识到在单片机的应用场景,本科生的毕业设计都能够在短时间内拿出一个能用的脚本执行环境。x86最早就有Basic伴生。单片机作为解释器来使用,大概是它在物联网时代,最适宜的用法。

4.我在xx个小时内,只解决了一个非常简单的逻辑错误,是不是意味着我无法胜任自己的工作?

解答:

my dear friend,工作至少有两类。一类是实体性工作;一类是复杂的问题处理。这两类工作要使用不同的策略来应对。对于实体性工作,时间投入越多,产出越多;对于复杂问题解析和调测,时间是无力的,放空的内心更重要。静坐比盯着屏幕会收效更大。同时,复杂问题是进行结对编程的最佳时机。如果你的源代码时常时过好多天才提交一次,在进行排错的时候,要提高你的提交密度。每一笔提交,都要有清晰的日志。不可有丝毫混淆。不可有我不知道它是怎么回事,但是它工作了的这种含混。错误发生时,解决费时费力,代价高昂。要让这些消耗,留下经验,错的有价值。还有,在两类工作都没有干完的时候,优先覆盖需求。转移注意力做其他事情,会更有助于疑难问题的处理。

5.下面哪个模块接口定义是更好的形式?

接口定义一:

typedef unsigned char cfg_addr_t;
typedef unsigned char CONFIG_ZONE;

cfg_addr_t getAddrOfConfigZone(CONFIG_ZONE zone);
cfg_addr_t getSizeOfConfigZone(CONFIG_ZONE zone);
void SaveConfig(CONFIG_ZONE cfgZone, cfg_addr_t offsetOfZoneIdxBase0, void *data, cfg_addr_t size);
void LoadConfig(CONFIG_ZONE cfgZone, cfg_addr_t offsetOfZoneIdxBase0, void *data, cfg_addr_t size);

void SaveDebugInfo1(cfg_addr_t offset, void *data, cfg_addr_t size);
void SaveDebugInfo2(cfg_addr_t offset, void *data, cfg_addr_t size);

接口定义二:

void CFG_LoadCalcParams(PPARAMS_I2KG obj);
float CFG_GetRatioOfI(void);
float CFG_GetRatioOfV(void);
uint16_t CFG_GetAD0(void);
float CFG_GetNetOffset(int idxBase0);
//"230717143032" = 23年7月17日14點30分32秒
void CFG_GetCalibrateTime(char *buf);
CALIBRATE_STATUS CFG_GetCalibrateStatus();

void CFG_SaveCalcParams(PPARAMS_I2KG obj);
void CFG_SetRatioOfI(float rhs);
void CFG_SetRatioOfV(float rhs);
void CFG_SetAD0(uint16_t rhs);
void CFG_SetNetOffset(int idxBase0, float rhs);
//"230717143032" = 23年7月17日14點30分32秒
void CFG_SetCalibrateTime(char *buf);

答:哪个是更好的形式,看模块的用户。对上位机编码而言,一般说来,你不可以把本可以隐藏的信息向你的用户暴露。这样做会增加维护成本,增大代码耦合,降低代码可阅读性。提高编码复杂性和出错机会。但是对于单片机编程而言,尽量不要行行重行行,乃至脱裤子放屁。第一种显得更直接。两者相较,建议仍然取后者。封装的原则是更高的原则,除非糟遇到空间和性能瓶颈,那个时候可以再谈优化。

6.维护代码的过程中发现代码结构有不妥,是否要即时修改?

答:改不改决定权在于时间。功能尚未覆盖,如果现在天色尚早,就改,如果已经临近deadline,那肯定不改。标记处这个你识别到的坏味道,然后忍耐(比如发现了一些代码本应该做拆分,但是聚拢成了一坨);如果预期这个问题可能对后续维护产生重大干扰。那也可能要改。关键是时间。代码好不好看,是次要的。

人的一些判断如果失去导航指引,会出现千奇百怪的盲目和武断作法,一时一地,非常清晰的认知和判断,换一种场景,换一种样貌出现,人们就会不认得它——你显然不会因为在上班的路上遇到一个个西瓜皮,就打算让全市居民停工一天,展开卫生大扫除,但是在编码时,你会死死盯住那块西瓜皮,无法解脱。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子正

thanks, bro...

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值