打印

单片机IO口操作又一方法(使用内联函数模板)

[复制链接]
5620|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
CC2530|  楼主 | 2011-3-18 17:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 CC2530 于 2011-3-18 17:28 编辑

主要思想是:
利用宏定义一些GPIO基本操作函数模板,
然后用模板去声明一个IO或者连续的IO区域,
那么对这个IO或者IO区域就可以使用模板内的所有函数.
且IO操作函数都是内联的,效率也非常高.
还有,没有使用的函数,也不会生成任何代码.


#ifndef __IO_AVR_ASSIGN_H__
#define __IO_AVR_ASSIGN_H__
#include "uni_int.h"
#include "iar_macro.h"
#define AVR_GPIO_ASSIGN(A, B, C) \
__always_inline__ static void   GPIO_##A##_MakeOut(uint8 x)     {(x)?(PORT##B |= _BV(C)):(PORT##B &= ~_BV(C)); DDR##B |= _BV(C);}\
__always_inline__ static void   GPIO_##A##_MakeHighImpedance(void)  {DDR##B &= ~_BV(C); PORT##B &= ~_BV(C);}\
__always_inline__ static void   GPIO_##A##_MakePullup(void)     {DDR##B &= ~_BV(C); PORT##B |=  _BV(C);}\
__always_inline__ static void   GPIO_##A##_Set(void)            {PORT##B |= _BV(C);}\
__always_inline__ static void   GPIO_##A##_Clr(void)            {PORT##B &= ~_BV(C);}\
__always_inline__ static void   GPIO_##A##_Toggle(void)         {PORT##B ^= _BV(C);}\
__always_inline__ static uint8  GPIO_##A##_Read(void)           {return ((PIN##B & _BV(C)) != 0);}\
__always_inline__ static uint8  GPIO_##A##_DdrRead(void)        {return ((DDR##B & _BV(C)) != 0);}
#define AVR_GPIOS_ASSIGN(A, B, C, D) \
__always_inline__ static void   GPIOS_##A##_MakeOut(uint8 x)   {PORT##B &=  ~_BVF_DATA(~x,C,D);PORT##B |=  _BVF_DATA(x,C,D); DDR##B |= _BVF( C,D);}\
__always_inline__ static void   GPIOS_##A##_MakeHighImpedance(void)    {DDR##B &= ~_BVF( C,D);PORT##B &= ~_BVF(C,D);}\
__always_inline__ static void   GPIOS_##A##_MakePullup(void)   {DDR##B &= ~_BVF( C,D);PORT##B |= _BVF(C,D);}\
__always_inline__ static void   GPIOS_##A##_Out(uint8 x)       {PORT##B &=  ~_BVF_DATA(~x,C,D);PORT##B |=  _BVF_DATA(x,C,D);}\
__always_inline__ static void   GPIOS_##A##_Toggle(void)       {PORT##B ^= _BVF( C,D);}\
__always_inline__ static uint8  GPIOS_##A##_Read(void)         {return (PIN##B & _BVF( C,D))>>C;}\
__always_inline__ static uint8  GPIOS_##A##_DdrRead(void)      {return (DDR##B & _BV( C))>>C ;}

#endif

一个例子:
AVR_GPIO_ASSIGN(LED_0,B,1);    //LED_0  : PB1
AVR_GPIO_ASSIGN(LED_1,B,2);    //LED_1  : PB2
AVR_GPIO_ASSIGN(LED_2,B,3);    //LED_2  : PB3
AVR_GPIO_ASSIGN(LED_3,B,4);    //LED_3  : PB4
AVR_GPIOS_ASSIGN(LED,B,1,4);   //LED,PB1到PB4(括号里最后的的4表示总共4个位)
volatile uint8 x;
int main()
{
   //LED单独位初始化
   GPIO_LED_0_MakeOut(0);   //LED_0设置为输出,初始输出低
   GPIO_LED_1_MakeOut(1);   //LED_1设置为输出,初始输出高
   GPIO_LED_2_MakeOut(0);   //LED_2设置为输出,初始输出低
   GPIO_LED_3_MakeOut(1);   //LED_3设置为输出,初始输出高
   
   //LED单独位输出
   GPIO_LED_0_Set();        //LED_0输出高
   GPIO_LED_0_Clr();        //LED_0输出低
   GPIO_LED_0_Toggle();     //LED_0翻转
   
   //LED单独位读取
   x=GPIO_LED_0_Read();     //读取LED_0
   x=GPIO_LED_0_DdrRead();  //读取LED_0方向
   
   
   
   //LED整体初始化
   GPIOS_LED_MakeOut((1<<1)|(1<<3));   
   //LED0,LED1,LED2,LED3设置为输出
   //初始LEDLED_1,LED_3输出高,LED_0,LED_2输出低
   
   //LED整体输出
   GPIOS_LED_Out(3);   //四位LED整体输出,LED0,LED1输出高,LED2,LED3输出低  
   GPIOS_LED_Out(12);  //四位LED整体输出,LED2,LED3输出高,LED0,LED1输出低
   GPIOS_LED_Toggle(); //四位LED整体翻转
   
   
   //LED整体读取
   x=GPIOS_LED_Read();     //读取四位LED
   x=GPIOS_LED_DdrRead();  //读取四位LED方向   
   
   
   
   while(1);
   
}

相关帖子

沙发
CC2530|  楼主 | 2011-3-18 17:22 | 只看该作者
本帖最后由 CC2530 于 2011-3-18 17:25 编辑

移植到CC2530:
#ifndef __IO_TI_CC8051_ASSIGN_H__
#define __IO_TI_CC8051_ASSIGN_H__
#include "uni_int.h"
#include "iar_macro.h"
#define TICC_GPIO_ASSIGN(A, B, C) \
__always_inline__ static void   GPIO_##A##_MakeNormal(void)   {P##B##SEL &= ~_BV(C);}\
__always_inline__ static void   GPIO_##A##_MakePeriph(void)   {P##B##SEL |=  _BV(C);}\
__always_inline__ static void   GPIO_##A##_MakeOut(uint8 x)   {(x)?(P##B |=_BV(C)):(P##B &= ~_BV(C)); P##B##DIR |= _BV( C);}\
__always_inline__ static void   GPIO_##A##_MakeHighImpedance(void)    {P##B##INP |= _BV( C);P##B##DIR &= ~_BV( C);}\
__always_inline__ static void   GPIO_##A##_MakePullup(void)   {P##B##INP &=~_BV( C); P##B##DIR &= ~_BV( C);}\
__always_inline__ static void   GPIO_##A##_Set(void)          {P##B |= _BV( C);}\
__always_inline__ static void   GPIO_##A##_Clr(void)          {P##B &= ~_BV( C);}\
__always_inline__ static void   GPIO_##A##_Toggle(void)       {P##B ^= _BV( C);}\
__always_inline__ static uint8  GPIO_##A##_Read(void)         {return ((P##B & _BV( C)) != 0);}\
__always_inline__ static uint8  GPIO_##A##_DirRead(void)      {return ((P##B##DIR & _BV( C)) != 0);}

#define TICC_GPIOS_ASSIGN(A, B, C, D) \
__always_inline__ static void   GPIOS_##A##_MakeNormal(void)   {P##B##SEL &= ~_BVF(C,D);}\
__always_inline__ static void   GPIOS_##A##_MakePeriph(void)   {P##B##SEL |=  _BVF(C,D);}\
__always_inline__ static void   GPIOS_##A##_MakeOut(uint8 x)   {P##B &=  ~_BVF_DATA(~x,C,D);P##B |=  _BVF_DATA(x,C,D); P##B##DIR |= _BVF( C,D);}\
__always_inline__ static void   GPIOS_##A##_MakeHighImpedance(void)    {P##B##INP |= _BVF( C,D);P##B##DIR &= ~_BVF( C,D);}\
__always_inline__ static void   GPIOS_##A##_MakePullup(void)   {P##B##INP &=~_BVF( C,D); P##B##DIR &= ~_BVF( C,D);}\
__always_inline__ static void   GPIOS_##A##_Out(uint8 x)       {P##B &=  ~_BVF_DATA(~x,C,D);P##B |=  _BVF_DATA(x,C,D);}\
__always_inline__ static void   GPIOS_##A##_Toggle(void)       {P##B ^= _BVF( C,D);}\
__always_inline__ static uint8  GPIOS_##A##_Read(void)         {return (P##B & _BVF( C,D))>>C;}\
__always_inline__ static uint8  GPIOS_##A##_DirRead(void)      {return (P##B##DIR & _BV( C))>>C ;}

#endif

TICC_GPIO_ASSIGN(LED_1,1,2);    //LED_1  : P1_2
TICC_GPIO_ASSIGN(LED_2,1,3);    //LED_2  : P1_3
TICC_GPIO_ASSIGN(LED_3,1,4);    //LED_3  : P1_4
TICC_GPIO_ASSIGN(LED_4,1,5);    //LED_4  : P1_5
TICC_GPIOS_ASSIGN(LEDS,1,2,4);  //LEDS   : P1_2-P1_5   

  volatile uint8 X;
   
   GPIO_LED_1_MakeHighImpedance();
   GPIO_LED_1_MakePullup();
   
   GPIO_LED_1_Set();
   GPIO_LED_1_Clr();
   GPIO_LED_1_Toggle();
   GPIOS_LEDS_MakeNormal();
   GPIOS_LEDS_MakePeriph();
   
   GPIOS_LEDS_Out(3);
   
   X=GPIOS_LEDS_Read();

使用特权

评论回复
板凳
ayb_ice| | 2011-3-18 19:00 | 只看该作者
cc2530是51核,本身支持位指令,你这真是画蛇添足

使用特权

评论回复
地板
流行音乐| | 2011-3-18 19:45 | 只看该作者
我还以为是 C++ 中的模板。

使用特权

评论回复
5
johnwjl| | 2011-3-18 19:46 | 只看该作者
平台的通用性与代码效率是一对矛盾。

使用特权

评论回复
6
CC2530|  楼主 | 2011-3-18 20:48 | 只看该作者
本帖最后由 CC2530 于 2011-3-18 21:02 编辑

3# ayb_ice

CC2530IO口寄存器只有P0,P1,P2能够直接用单独位,其他寄存器不能。
IO口操作不仅仅只是P0,P1,P2的操作,还有功能寄存器,方向寄存器,是否使能上下拉电阻等问题。

比如说要把P1_0设置输出口,并输出1,

要用:
P1SEL &= ~(1<<0);
P1_0=1;
P1DIR |= 1<<0;

这样写,意义名不明确,P1_0到底代表什么,没有说明。

进一步写成这样:
#define LED_0 P1_0
P1SEL &= ~(1<<0);
LED_0=1;
P1DIR |= 1<<0;

这样写又有点不伦不类。

进一步改写成这样:
#define LED_0 0
P1SEL &= ~(1<<LED_0);
P1  |=1<<LED_0;
P1DIR |= 1<<LED_0;

这样又有个麻烦,如果LED很多,且在不同的端口上,也很麻烦。

还是用函数模板最简单。
不管多少个IO口,IO口在哪个端口上,用模板声明一次,就可以用所有的函数,意义也明确。
TICC_GPIO_ASSIGN(LED_0,1,0);    //LED_0  : P1_0

//程序中直接对LED_0操作,不用再理会LED_0具体是在哪个端口上,具体是哪个位。
GPIO_LED_0_MakeNormal();    //LED_0设置为通用IO口
GPIO_LED_0_MakeOut(1);       //LED_0设置为输出,并初始输出高

还有,不用担心效率问题。
因为都是内联函数,编译器会优化,
与你直接用寄存器或者寄存器位效率是一样的。

使用特权

评论回复
7
CC2530|  楼主 | 2011-3-18 20:54 | 只看该作者
本帖最后由 CC2530 于 2011-3-18 20:56 编辑

比如CC2530实际一个产品中:

#ifndef __PERIPH_IO_H__
#define __PERIPH_IO_H__
#include "io_ticc_assign.h"

#define DIO_1      CC2530_PIN_19  //P0_0
#define DIO_2      CC2530_PIN_18  //P0_1 IN1
#define DIO_3      CC2530_PIN_5   //P1_5 IN2
#define DIO_4      CC2530_PIN_35  //P2_1 DD IN3 SW3
#define DIO_5      CC2530_PIN_34  //P2_2 DC IN4 SW4

#define DIO_6      CC2530_PIN_38  //P1_6
#define DIO_7      CC2530_PIN_8   //P1_2
#define DIO_8      CC2530_PIN_37  //P1_7
#define DIO_9      CC2530_PIN_9   //P1_1

#define DIO_10     CC2530_PIN_36  //P2_0
#define DIO_11     CC2530_PIN_7   //P1_3

#define ADC_3      CC2530_PIN_15  //P0_4
#define ADC_2      CC2530_PIN_14  //P0_5
#define ADC_1      CC2530_PIN_13  //P0_6
#define ADC_0      CC2530_PIN_12  //P0_7

#define LED_1      DIO_6   //P1_6
#define LED_2      DIO_7   //P1_2
#define LED_3      DIO_8   //P1_7
#define LED_4      DIO_9   //P1_1

#define KEY_1      DIO_2   //P0_1
#define KEY_2      DIO_3   //P1_5
#define KEY_3      DIO_4   //P2_1
#define KEY_4      DIO_5   //P2_2

TICC_GPIO_ASSIGN(DIO_1,0,0);
TICC_GPIO_ASSIGN(DIO_2,0,1);
TICC_GPIO_ASSIGN(DIO_3,1,5);
TICC_GPIO_ASSIGN(DIO_4,2,1);

TICC_GPIO_ASSIGN(DIO_5,2,2);
TICC_GPIO_ASSIGN(DIO_6,1,6);
TICC_GPIO_ASSIGN(DIO_7,1,2);
TICC_GPIO_ASSIGN(DIO_8,1,7);

TICC_GPIO_ASSIGN(DIO_9,1,1);
TICC_GPIO_ASSIGN(DIO_10,2,0);
TICC_GPIO_ASSIGN(DIO_11,1,3);

TICC_GPIO_ASSIGN(LED_1,1,6);
TICC_GPIO_ASSIGN(LED_2,1,2);
TICC_GPIO_ASSIGN(LED_3,1,7);
TICC_GPIO_ASSIGN(LED_4,1,1);

TICC_GPIO_ASSIGN(KEY_1,0,1);
TICC_GPIO_ASSIGN(KEY_2,1,5);
TICC_GPIO_ASSIGN(KEY_3,2,1);
TICC_GPIO_ASSIGN(KEY_4,2,2);


#endif

这样定义后,我对任意IO口操作都非常方便,而且不用管IO口到底在那个端口上,具体是哪个位。

使用特权

评论回复
8
ayb_ice| | 2011-3-19 08:39 | 只看该作者
LS
你这方便只是表面现象
IO的方向,上下拉等操作一般只是初始化的时候弄一下,中间大部分只是操作IO数据寄存器,而这些都是可位寻址的,至于其它SFR寄存器,不管你怎么做,真正要使用那个模块时都要对着手册看的,哪怕是标准的51,我也不相信有谁能记住所有寄存器定义,何况现在的增强型51,关键是写好解释,何况还要移植
这样的操作其实用宏定义更方便,而且通用

使用特权

评论回复
9
CC2530|  楼主 | 2011-3-19 09:15 | 只看该作者
本帖最后由 CC2530 于 2011-3-19 09:58 编辑

看来是编程习惯不同,得出的结论却大大不同.
当然,以上仅仅是个人看法.

使用特权

评论回复
10
ayb_ice| | 2011-3-19 09:38 | 只看该作者
移植到IAR的EW8051可以用标准C的位域,然后用个宏实现多个语句操作即可

使用特权

评论回复
11
batsong| | 2011-3-19 09:56 | 只看该作者
难道比菜农的红杏还牛B,待我学习一下

使用特权

评论回复
12
CC2530|  楼主 | 2011-3-19 10:08 | 只看该作者
本帖最后由 CC2530 于 2011-3-19 10:25 编辑
移植到IAR的EW8051可以用标准C的位域,然后用个宏实现多个语句操作即可
ayb_ice 发表于 2011-3-19 09:38



本质上是一样.
都是用宏定义IO口的一系列操作.

这里我只是说,用模板比一般的宏定义更方便一些.

一般的宏定义,对于一个IO口操作,就要定义多个宏.
假定一个IO口要定义5个宏:设置输入,设置输出,输出高,输出低,翻转.
20个IO口操作,有可能要定义20 * 5 =100个宏.
实际可能不需要要怎么多,但显然要远远大于20次.

如果用模板,每个IO口用模板定义一次,就相当于
对这个IO口定义了模板里所有的宏.
20个IO口定义20个宏,就相当于对IO口单独定义宏100次.

这就是我一直强调模板的原因.

C语言并没有模板概念,这里借用C++的概念.






使用特权

评论回复
13
CC2530|  楼主 | 2011-3-19 10:55 | 只看该作者
这种模板思想,在C语言其他地方也能使用.

使用特权

评论回复
14
yzq13246068880| | 2020-5-21 13:24 | 只看该作者
feichanglihai

使用特权

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

本版积分规则

0

主题

262

帖子

1

粉丝