打印
[ZLG-MCU]

转一篇如何写单片机程序的**

[复制链接]
3155|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
金鱼木鱼|  楼主 | 2011-9-27 21:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
此文我保存了先

写单片机程序也是程序,也要遵循写软件的一些基本原则,不是为了完成功能那么简单。我看过的所有的C语言单片机书籍基本都不注重模块化思想,完全是拿着C当汇编用,简直是在糟蹋C语言!

如下问题,几乎所有的单片机书籍中都大量存在(更别说网上的和现实中的代码了,书上都写的那么差劲,学的人能好到哪里去)
1、变量到处定义,根本不管变量的生命周期是否合适(请回答:全局变量、局部变量、静态变量、volatile变量有什么区别联系?)
2、变量名称极不规范,根本从名字上看不出来这个变量类型是什么,到底想干什么。
3、函数定义几乎不用参数,全都是void
4、语句写的一点都不直观,根本就是在用汇编。比如:想取一个字长的高字节和低字节,应该定义一个宏或是函数来做,如#define HIBYTE(w) ((BYTE)((DWORD)(w) >> 8)),以后直接用HIBYTE()多直观,难道非得用(BYTE)((DWORD)(w) >> 8)代表你的移位操作的水平很高吗?
5、最重要的一点,没有建立模块化的编程思想。一个程序往往要很多部分协同工作,需要把不同的功能分离出来单独创建一个.h.c的文件,然后在头文件中把可以访问的函数暴露出来。
6、不思考曾经做过的程序是否还有改进的余地,写程序如果只是为了写而写,一辈子也长进不了多少



为了证明我以上的观点,特此发一下我对c51定时器的封装,此定时器可以同时设定多个定时任务,定时精度由晶振精度决定。我的项目中一般用的是12MHZ的晶振,最小定时在20ms基本可以接受,再小的不能保证。

相关帖子

沙发
金鱼木鱼|  楼主 | 2011-9-27 21:54 | 只看该作者
/////////////////////////////////////////头文件//////////////////////////////////////////#ifndef _TIMER_CONFIG_H_#define _TIMER_CONFIG_H_#include "const.h"#include "oscfrequencydef.h"#ifndef OSC_FREQUENCY#error undefined OSC_FREQUENCY#endif//#warning must be used in AT89C52 or later version because of "idata"#warning **********************************************************************************#warning !! make sure MAX_TIMER_EVENT_NUM and TIMER0_BASE_INTERVAL has appropriate value!!  #warning **********************************************************************************/****************************************************************************定时中断每TIMER0_BASE_INTERVAL毫秒产生一次,用户定义的中断时间必须是它的整数倍****************************************************************************/#define MAX_TIMER_EVENT_NUM 5 //可设置不同定时事件的最大个数(至少为2)#define TIMER0_BASE_INTERVAL 20 //单位:毫秒

使用特权

评论回复
板凳
金鱼木鱼|  楼主 | 2011-9-27 21:54 | 只看该作者
typedef void (*TIMERPROC)(BYTE nID);

void InitTimer0();
BOOL SetTimerCallback(TIMERPROC lpTimerFunc); //必须在SetTimer0之前调用
BOOL SetTimer0(BYTE nID, WORD wInterval); //通过nID(nID>0)来区分  
//BOOL KillTimer0(BYTE nID);





/////////////////////////////////////////////////
//以下为内部使用

typedef struct tagTIMERINFO
{
BYTE nID;         //定时器ID
WORD wInterval;         //此定时器的设定间隔时间
WORD wElapse;         //剩余的时间

}TIMERINFO;
static BOOL AddTail(const TIMERINFO* pTimerInfo);
static BOOL Remove(BYTE nID);
static BYTE FindID(BYTE nID);

#endif


其中用到的的const.h定义如下:


#ifndef _CONST_H_
#define _CONST_H_
#include <intrins.h>

#define TRUE 1
#define FALSE 0

typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef unsigned long DWORD;
typedef float FLOAT;         
typedef char CHAR;
typedef unsigned char UCHAR;
typedef int INT;
typedef unsigned int UINT;
typedef unsigned long ULONG;
typedef UINT WPARAM;
typedef ULONG LPARAM;
typedef ULONG LRESULT;
typedef void VOID;
typedef const CONST;
typedef void *PVOID;
typedef bit BOOL;  




#define MAKEWORD(lo, hi) ((WORD)(((BYTE)(lo)) | ((WORD)((BYTE)(hi))) << 8))
#define MAKEDWORD(lo, hi) ((DWORD)(((WORD)(lo)) | ((DWORD)((WORD)(hi))) << 16))
#define LOWORD(l) ((WORD)(l))
#define HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))
#define LOBYTE(w) ((BYTE)(w))
#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))


#define SET_STATE_FLAG(state, mask) ((state) |= (mask))
#define        RESET_STATE_FLAG(state, mask) ((state) &= ~(mask))
#define TEST_STATE_FLAG(state, mask) ((state) & (mask))

使用特权

评论回复
地板
金鱼木鱼|  楼主 | 2011-9-27 21:55 | 只看该作者
#define TEST_BIT(b, offset) (1 & ((b) >> (offset)))#define SET_BIT(b, offset) ((b) |= (1 << (offset)))#define RESET_BIT(b, offset) ((b) &= (~(1 << (offset))))//将BCD码变为十进制,如将0x23变为23//注意:高四位和低四位均不能大于9#define BCD_TO_DECIMAL(bcd) ((BYTE)((((BYTE)(bcd)) >> 4) * 10 + (((BYTE)(bcd)) & 0x0f)))#define DECIMAL_TO_BCD(decimal) ((BYTE)(((((BYTE)(decimal)) / 10) << 4) | ((BYTE)(decimal)) % 10))#define NOP() _nop_()#define BYTE_ROTATE_LEFT(b, n) _crol_(b, n)#define BYTE_ROTATE_RIGHT(b, n) _cror_(b, n)        #define WORD_ROTATE_LEFT(w, n) _irol_(w, n)#define WORD_ROTATE_RIGHT(w, n) _iror_(w, n)  #define DWORD_ROTATE_LEFT(dw, n) _lrol_(dw, n)#define DWORD_ROTATE_RIGHT(dw, n) _lror_(dw, n)#define ENABLE_ALL_INTERRUPTS() (EA = 1)#define DISABLE_ALL_INTERRUPTS() (EA = 0)#endif

使用特权

评论回复
5
金鱼木鱼|  楼主 | 2011-9-27 21:55 | 只看该作者
面是定时器的.c文件的具体实现:

实现中用到了一点数据结构中队列的概念

#include "timerconfig.h"
#include "chiptypedef.h"
#include <limits.h>
#include <string.h>



code const WORD TIMER0_INIT_VALUE = UINT_MAX - ((WORD)((float)OSC_FREQUENCY * 1.0f / 12 * 1000)) * TIMER0_BASE_INTERVAL;


idata TIMERINFO TimerInfoArray[MAX_TIMER_EVENT_NUM] = {0};
TIMERPROC g_pfnTimerFunc = NULL;
BYTE g_nTimerInfoNum = 0;         //当前队列的元素个数


void InitTimer0()
{
TMOD |= T0_M0_; //定时器0,工作方式1
TH0 = HIBYTE(TIMER0_INIT_VALUE);
TL0 = LOBYTE(TIMER0_INIT_VALUE);
TR0 = 0;         //停止定时器0
ET0 = 0;         //关定时器0中断        
EA = 1;
}

BOOL SetTimerCallback(TIMERPROC lpTimerFunc)
{
if(lpTimerFunc == NULL)
return FALSE;


g_pfnTimerFunc = lpTimerFunc;        

return TRUE;        

}

BOOL SetTimer0(BYTE nID, WORD wInterval)
{
TIMERINFO ti;
if(g_pfnTimerFunc == NULL || nID == 0 || wInterval == 0)
return FALSE;

if(wInterval % TIMER0_BASE_INTERVAL != 0) //定时间隔必须是TIMER0_BASE_INTERVAL的整数倍
return FALSE;

if(FindID(nID) != MAX_TIMER_EVENT_NUM)        //若已经有相同的ID存在
return FALSE;




ti.nID = nID;
ti.wInterval = wInterval;
ti.wElapse = wInterval;

if(!AddTail(&ti))
return FALSE;

TR0 = 1;         //启动定时器0
ET0 = 1;         //开定时器0中断        

return TRUE;

}
  /*
BOOL KillTimer0(BYTE nID)
{

if(!Remove(nID) || nID == 0)
return FALSE;

if(g_nTimerInfoNum == 0) //若最后一个定时事件已经停止,则关定时器中断
ET0 = 0;

return TRUE;        
} */

static BYTE FindID(BYTE nID)
{
BYTE i = 0;
for(i = 0; i < MAX_TIMER_EVENT_NUM; i++)

使用特权

评论回复
6
金鱼木鱼|  楼主 | 2011-9-27 21:55 | 只看该作者
{
if(TimerInfoArray.nID == nID)
return i;
}

return MAX_TIMER_EVENT_NUM;        
}

static BOOL AddTail(const TIMERINFO* pTimerInfo)
{
if(g_nTimerInfoNum == MAX_TIMER_EVENT_NUM || pTimerInfo == NULL)
return FALSE;


memcpy(&TimerInfoArray[g_nTimerInfoNum], pTimerInfo, sizeof(TIMERINFO));        
g_nTimerInfoNum++;

return TRUE;        
}

/*
static BOOL Remove(BYTE nID)
{
BYTE nIndex = FindID(nID);
BYTE nRest = g_nTimerInfoNum - nIndex - 1;

if(nIndex == MAX_TIMER_EVENT_NUM || nID == 0)
return FALSE;        

if(nRest == 0) //已经是队列尾元素
{
memset(&TimerInfoArray[nIndex], 0, sizeof(TIMERINFO));        
}
else
{
//删除后,前移
memcpy(&TimerInfoArray[nIndex], &TimerInfoArray[nIndex + 1], sizeof(TIMERINFO) * nRest);
memset(&TimerInfoArray[nIndex + nRest], 0, sizeof(TIMERINFO) * (MAX_TIMER_EVENT_NUM - (nIndex + nRest) - 1));        
}

g_nTimerInfoNum--;

return TRUE;

} */

void Timer0ISR() interrupt TF0_VECTOR
{
BYTE i = 0;
TF0 = 0;

TH0 = HIBYTE(TIMER0_INIT_VALUE);        
TL0 = LOBYTE(TIMER0_INIT_VALUE);

for(i = 0; i < g_nTimerInfoNum; i++)
{
TimerInfoArray.wElapse -= TIMER0_BASE_INTERVAL;
if(TimerInfoArray.wElapse == 0)
{
(*g_pfnTimerFunc)(TimerInfoArray.nID);        
TimerInfoArray.wElapse = TimerInfoArray.wInterval;        
}

}
}

使用特权

评论回复
7
3B1105| | 2011-9-27 23:30 | 只看该作者
不错,看过后,很受益匪浅

使用特权

评论回复
8
小小子他爹| | 2011-9-27 23:36 | 只看该作者
这才是真正的编程者,其他人都是码字的。

使用特权

评论回复
9
frlop| | 2011-9-28 09:09 | 只看该作者
虽然还不是很懂 但受教了
mark。

使用特权

评论回复
10
yezhubenyue| | 2011-9-28 09:57 | 只看该作者
好帖子收藏了。。。

使用特权

评论回复
11
sysdriver| | 2011-9-28 12:28 | 只看该作者
模块化的思想真的是这样吗?大部分只是编程规范而已。

想提高编程能力的:多做项目,多看优秀的项目,模仿学习优秀的项目,多学理论基础。

使用特权

评论回复
12
gdutxzy| | 2011-10-2 17:22 | 只看该作者
用汇编已经在写模块了,学的时候老师就是这样教的。

使用特权

评论回复
13
bairan168| | 2011-10-16 21:17 | 只看该作者
留印,慢慢看。

使用特权

评论回复
14
chencheng| | 2011-10-17 12:54 | 只看该作者
很赞同其中很多观点,比如模块化,比如#define HIBYTE(w) ((BYTE)((DWORD)(w) >> 8)),等等。但一味强调C的形式,就变成了程序员!注意,你是在对单片机写程序,你是懂单片机的,而且不管多么规范的程序,最终都要落实到单片机有限的那些指令上。为什么要用不懂硬件的程序员的要求来作为硬件工程师的编程标准了?

使用特权

评论回复
15
LONGXIA| | 2012-9-16 09:17 | 只看该作者
MARK

使用特权

评论回复
16
tanzhennian| | 2012-9-22 16:35 | 只看该作者
受教了,如拨云见日呀

使用特权

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

本版积分规则

个人签名:http://bbs.srvee.com/forum-104-1.html

346

主题

1551

帖子

2

粉丝