4585|0

367

帖子

0

TA的资源

裸片初长成(高级)

楼主
 

AVR c语言优秀编程风格 [复制链接]

文件结构

模块化的程序应该是有一个很好的程序结构的。AVR C语言程序有两种用户文件,.c程序文件,.h头文件,程序中编写过程中需要在.c文件中包含.h头文件。初学者往往出现重复包含或者头文件包含错误的问题,我当时也时常为这种错误而发愁。下面我以我写的电机驱动例程来给大家说明一下,优秀的编程文件结构。

这个工程中有8个文件,一个说明文件,如下图:下载程序例子 电机控制案例 。



我写的成型的程序的文件个数基本上都是偶数,因为每一个结构化的函数定义.c文件都会对应一个.h文件。main.c对应config.h。我们来看看各文件的包含关系。下面我们看看这些文件的包含关系与内容:[推荐的文件包含顺序与关系]

所有.c文件都包含了config.h文件。如: #include "config.h"
在config.h 中有如下代码:

#include "delay.h"
#include "device_init.h"
#include "motor.h"

这样做就不容易出现错误的包含关系,为了预防万一,我们还引入了宏定义与预编译。如下:

#ifndef _UNIT_H__
#define _UNIT_H__ 1
//100us
extern void Delay100us(uint8 n);
//1s
extern void Delay1s(uint16 n); // n <= 6 ,when n==7, it is 1.
//1ms
extern void Delay1ms(uint16 n);

#endif

第一次包含本文件的时候正确编译,并且#define _UNIT_H__ 1,第二次包含本文件#ifndef _UNIT_H__就不再成立,跳过文件。
预编译还有更多的用途,比如可以根据不同的值编译不同的语句,如下:

//#pragma REGPARMS
#if CPU_TYPE == M128
#include
#endif
#if CPU_TYPE == M64
#include
#endif
#if CPU_TYPE == M32
#include
#endif
#if CPU_TYPE == M16
#include
#endif
#if CPU_TYPE == M8
#include
#endif

#include 与 #include "filename" 的区别 :前者是包含系统目录include下 的文件,后者是包含程序目录下的文件。
变量名与函数名

变量以及函数命名应该按照尽量短,按需长,具有实际意义。可以通过下划线或者大小写结合的方法组合动词和名词组成变量函数名。下面对比好的命名方法与不好的命名方法:

好的:   Delay100us();
不好的:   Yanshi();
好的:   init_devices();
不好的:   Chengxuchushihua();
好的:   int temp;
不好的:   int dd;
外部调用

首先在模块化程序的.h文件中定义extern

//端口初始化
extern void port_init(void);

//T2初始化
void timer2_init(void);

//各种参数初始化
extern void init_devices(void);模块化程序的.c文件中定义函数,不要在模块化的程序中调用程序,及不要出现向timer2_init();这样函数的使用,因为你以后不知道你到底什么地方调用了函数,导致程序调试难度增加。可以在定义函数的过程中调用其他函数作为函数体。

/**************************采用timer2 产生波形***********************/
// PWM频率 = 系统时钟频率/(分频系数*2*计数器上限值))
void timer2_init(void)
{
TCCR2 = 0x00; //stop
TCNT2= 0x01; //set count
OCR2 = 0x66; //set compare
TCCR2 = (1< //占空比=高比低为:(OCR2-0X01)/(0XFF-OCR2)   OX01++++++(OCR2)__________OXFF (+表示输出高,_表示输出低)
//即OCR2越大,输出越大
}在少数几个文件中调用函数,在main.c中调用大部分函数,在interupts.c中根据不同的中断调用服务函数。

void main(void)
{

/******************************************************************************/
//初始工作
/******************************************************************************/
init_devices();

while(1)
{
  for_ward(0);           //默认速度运转 正
  Delay1s(5);           //延时5s
  motor_stop();         //停止
  Delay1s(5);           //延时5s
  back_ward(0);           //默认速度运转 反
  Delay1s(5);           //延时5s
  speed_add(20);         //加速
  Delay1s(5);           //延时5s
  speed_subtract(20);     //减速
  Delay1s(5);           //延时5s
}
  
}宏定义

宏定义主要用于两个地方:

一是用得非常多的命令或语句,利用宏将其简化。

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0
#endif
#define MIN(a,b)         ((a #define MAX(a,b)         ((a>b)?(a):(b))
#define ABS(x)         ((x>)?(x):(-x))
typedef unsigned char uint8;                       /* 定义可移植的无符号8位整数关键字         */
typedef signed   char int8;                         /* 定义可移植的有符号8位整数关键字         */
typedef unsigned int   uint16;                       /* 定义可移植的无符号16位整数关键字       */
typedef signed   int   int16;                       /* 定义可移植的有符号16位整数关键字       */
typedef unsigned long uint32;                       /* 定义可移植的无符号32位整数关键字       */
typedef signed   long int32;                       /* 定义可移植的有符号32位整数关键字       */
二是利用宏定义方便的进行硬件接口操作,再程序需要修改时,只需要修改宏定义即可,而不需要满篇去找命令行,进行修改。

//PD4,PD5 电机方向控制 如果更改管脚控制电机方向,更改PORTD |= 0x10即可。
#define moto_en1 PORTD |= 0x10
#define moto_en2 PORTD |= 0x20
#define moto_uen1 PORTD &=~ 0x10
#define moto_uen2 PORTD &=~ 0x20
//启动TC2定时比较和溢出
#define TC2_EN TIMSK |= (<<1OCIE2)|(1< //禁止TC2再定时比较和溢出
#define TC2_DIS TIMSK &=~ (1<
为了增加程序的可读性,方便合作者读动程序,或者程序作者在一段时间之后还能看懂程序,我们需要在程序中写 注释。

在比较特殊的函数使用或者命令调用的地方加单行注释。使用方法为:

Tbuf_putchar(c,RTbuf);       // 将数据加入到发送缓冲区并开中断
extern void Delay1s(uint16 n); // n <= 6 ,when n==7, it is 1. 在模块化的函数中使用详细段落注释:

/***********************
** 函数名称: Com_putchar
** 功能描述: 从串行口输出一个字符c
** 输 入: c:输出字符
** 输出   : 0:失败 1:成功
** 全局变量: 无
** 调用模块:
** 说明:
** 注意:
********************/

在文件头上加文件名,文件用途,作者,日期等信息。

/*********************************************************************************************************
**                 serial   driver
**               (c) Copyright 2005-2006, limaokui
**                   All Rights Reserved
**
**                       V1.1.0
**
**
**--------------文件信息--------------------------------------------------------------------------------
**文   件   名:sio.c
**创   建   人: 李茂奎
**最后修改日期: 2005年7月13日
**描   述: serial   driver
**
**--------------历史版本信息----------------------------------------------------------------------------
** 创建人: 李茂奎
** 版 本: V1.00
** 日 期: 2005年7月13日
** 描 述: 原始版本
**
*********************************************************************************************************/

要清楚,注释是为了方便阅读,增强程序的可度性,不要本末倒置,不要给很简单大家都能看明白的程序加注释,不要让注释淹没了你的程序结构。对于函数,变量等尽量使用文件名自注释的方法,及通过文件名就可以知道意思。

[ 本帖最后由 黑衣人 于 2008-9-11 11:07 编辑 ]
点赞 关注
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表