历史上的今天
返回首页

历史上的今天

今天是:2025年02月04日(星期二)

2020年02月04日 | AVR的C语言优秀编程风格

2020-02-04 来源:eefocus

有一位编程大师,他写非结构化的程序,一位初学者刻意模仿他,也写非结构化的程序。当他让大师看他的进步时,大师批评了他的非结构化程序:“ 对一位编程大师合适的东西未必对一个初学者同样合适,在超越结构化之前,你必须理解编程之道。” 我个人认为作为一个初学者应该踏踏实实的打好程序设计的基础,不要急功近利,舍本逐末。我走过不少弯路,希望大家能和我一样能牢记编程大师的忠告:“对编程大师合适的东西未必对一个初学者同样合适”。


本文所描述的优秀编程风格适合于大部分语言,文章中可能提到你不是很了解的概念,没有关系,你放心的读下去,当你使用AVR一个月之后,你什么都明白了。


AVR c语言优秀编程风格


文件结构


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


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


所有.c文件都包含了config.h文件。如: #i nclude "config.h"

在config.h 中有如下代码:

#i nclude "delay.h"

#i nclude "device_init.h"

#i nclude "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

#i nclude

#endif

#if CPU_TYPE == M64

#i nclude

#endif

#if CPU_TYPE == M32

#i nclude

#endif

#if CPU_TYPE == M16

#i nclude

#endif

#if CPU_TYPE == M8

#i nclude

#endif

#i nclude 与 #i nclude "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日

** 描述: 原始版本

** 

*********************************************************************************************************/

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

推荐阅读

史海拾趣

Holmes Co Inc公司的发展小趣事

面对日益增长的全球市场需求,Holmes Electronics决定实施全球化战略。他们首先在亚洲建立了生产基地,利用当地的成本优势和供应链资源,大幅提高生产效率并降低成本。随后,公司又在欧洲和美国设立了研发中心和销售网络,以更好地服务当地客户并捕捉市场机会。这一全球化战略不仅使Holmes Electronics的产品遍布全球,还增强了公司的市场响应能力和品牌影响力。

Frequency Management International公司的发展小趣事

随着业务的不断发展,FMI开始积极拓展国内外市场。公司凭借高品质的产品和专业的服务,赢得了众多客户的信赖和支持。同时,FMI还积极实施国际化战略,通过参加国际展会、建立海外销售网络等方式,不断提升品牌国际影响力。目前,FMI的产品已经广泛应用于计算机外设、工业仪器、局域网/广域网、光网络、过程控制、电信和无线产品等多个领域,并远销全球多个国家和地区。

AMERICASEMI [America Semiconductor, LLC]公司的发展小趣事

AMERICASEMI公司成立于XXXX年,起初是一家专注于半导体技术研发的小型创业公司。在创立初期,公司面临着资金短缺、市场竞争激烈等诸多挑战。然而,凭借创始团队对半导体技术的深刻理解和市场需求的敏锐洞察,AMERICASEMI成功开发出一系列具有竞争力的半导体产品,并逐渐在市场中占据了一席之地。

FORYARD公司的发展小趣事

随着业务的不断扩展,FORYARD意识到全球化布局的重要性。1995年,公司决定在中国设立研发中心和生产基地,以利用当地丰富的人才资源和成本优势。这一战略决策极大地提升了FORYARD的产能和研发效率,同时也使其能够更好地服务全球客户。此后,FORYARD还陆续在欧洲、亚洲等地建立了多个分支机构,形成了覆盖全球的研发、生产和销售网络。通过全球化布局,FORYARD不仅增强了自身的市场竞争力,还促进了全球电子产业的交流与合作。

Holy Stone公司的发展小趣事

近年来,Holy Stone不断加大在技术研发和产品创新方面的投入。公司致力于研发适用于节能产品应用所需的被动元件,帮助客户提升产品能源转换效率。在全球汽车电子化发展的背景下,Holy Stone正式跨入车用电子供应链,以提供优质元件的能力协助汽车电子达到最佳数位化。这些技术创新不仅推动了公司产品的升级换代,还为公司开拓了新的市场领域和增长点。

以上五个故事展示了Holy Stone公司在电子行业中的发展历程和成就,每个故事都体现了公司在不同方面的努力和成果。

达方(DARFON)公司的发展小趣事

进入21世纪后,达方电子在技术研发上取得了重大突破,成功研发出多款具有竞争力的新产品。同时,公司积极开拓国际市场,通过与国际知名品牌的合作,将产品销往全球各地。这一阶段的成功,不仅使达方电子的营收大幅增长,也进一步提升了公司的品牌影响力。

问答坊 | AI 解惑

一道面试题,供大家讨论

求理想运算放大电路的放大倍数。 …

查看全部问答>

DSPIC的SPI口连接DAC的问题,大家帮帮忙!急

单片机用的是DSPIC3013f,DAC是MCP4821,MCP4821的SDI接单片机的SDO,SCK接SCK,CS接RB2,LDAC接地,程序如下: #include \"p30f3013.h\" _FOSC(CSW_FSCM_OFF&FRC_PLL4); _FWDT(WDT_OFF); _FBORPOR(PBOR_OFF&MCLR_EN); _FGS(CODE_PROT_OFF); unsig ...…

查看全部问答>

单片机选型求推荐

要体积小些的,带ADC(》10bit)和USB控制器的那种谢谢…

查看全部问答>

现在还有人用UPSD么?

                                  …

查看全部问答>

我的STM32总是出现JTAGCOMMUNICATIONFAILURE

                                 有时还出现NO ULINK DEVICE FOUND,请知道的大侠指教一下。…

查看全部问答>

STM32中CRC模块的CRC_IDR寄存器作什么用?

                                 CRC_IDR寄存器不知道有什么用处?…

查看全部问答>

STM32单路DAC的速度最快是多少?

                                 例如采用72MHz的时钟,模拟信号的建立时间最短是多少?谢谢!…

查看全部问答>

stm32 计数 如何设置成双边缘触发模式

不知道stm32计数模式下,可不可以设置成双边缘触发模式,若可以,麻烦告诉下方法(C语句),谢谢!…

查看全部问答>

【DIY手机】+ddllxxrr

本帖最后由 ddllxxrr 于 2014-5-16 12:12 编辑 我想用STM32F429I-Discovery和GSM模块及音频的输入输出,来实现一个手机方案。 1、说明你想实现的功能 实现打接电话的功能 2、列出所需硬件 STM32F429I-Discovery 开发板一块 GSM模块一个。 ...…

查看全部问答>

关于设计一个声音定位系统的,程序问题,希望能帮忙指点指点!多谢!

本帖最后由 dontium 于 2015-1-23 11:37 编辑 一、任务 设计一套声音定位系统。在一块不大于1m2的平板上贴一张500mm×350mm的坐标纸,在其四角外侧分别固定安装一个声音接收模块,声音接收模块通过导线将声音信号传输到信息处理模块,声音定位系 ...…

查看全部问答>