单片机IO口操作又一方法(使用内联函数模板)
本帖最后由 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 uint8GPIO_##A##_Read(void) {return ((PIN##B & _BV(C)) != 0);}\
__always_inline__ static uint8GPIO_##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 uint8GPIOS_##A##_Read(void) {return (PIN##B & _BVF( C,D))>>C;}\
__always_inline__ static uint8GPIOS_##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: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 uint8GPIO_##A##_Read(void) {return ((P##B & _BV( C)) != 0);}\
__always_inline__ static uint8GPIO_##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 uint8GPIOS_##A##_Read(void) {return (P##B & _BVF( C,D))>>C;}\
__always_inline__ static uint8GPIOS_##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(); cc2530是51核,本身支持位指令,你这真是画蛇添足 我还以为是 C++ 中的模板。 平台的通用性与代码效率是一对矛盾。 本帖最后由 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设置为输出,并初始输出高
还有,不用担心效率问题。
因为都是内联函数,编译器会优化,
与你直接用寄存器或者寄存器位效率是一样的。 本帖最后由 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口到底在那个端口上,具体是哪个位。 LS
你这方便只是表面现象
IO的方向,上下拉等操作一般只是初始化的时候弄一下,中间大部分只是操作IO数据寄存器,而这些都是可位寻址的,至于其它SFR寄存器,不管你怎么做,真正要使用那个模块时都要对着手册看的,哪怕是标准的51,我也不相信有谁能记住所有寄存器定义,何况现在的增强型51,关键是写好解释,何况还要移植
这样的操作其实用宏定义更方便,而且通用 本帖最后由 CC2530 于 2011-3-19 09:58 编辑
看来是编程习惯不同,得出的结论却大大不同.
当然,以上仅仅是个人看法. 移植到IAR的EW8051可以用标准C的位域,然后用个宏实现多个语句操作即可 难道比菜农的红杏还牛B,待我学习一下 本帖最后由 CC2530 于 2011-3-19 10:25 编辑
移植到IAR的EW8051可以用标准C的位域,然后用个宏实现多个语句操作即可
ayb_ice 发表于 2011-3-19 09:38 https://bbs.21ic.com/images/common/back.gif
本质上是一样.
都是用宏定义IO口的一系列操作.
这里我只是说,用模板比一般的宏定义更方便一些.
一般的宏定义,对于一个IO口操作,就要定义多个宏.
假定一个IO口要定义5个宏:设置输入,设置输出,输出高,输出低,翻转.
20个IO口操作,有可能要定义20 * 5 =100个宏.
实际可能不需要要怎么多,但显然要远远大于20次.
如果用模板,每个IO口用模板定义一次,就相当于
对这个IO口定义了模板里所有的宏.
20个IO口定义20个宏,就相当于对IO口单独定义宏100次.
这就是我一直强调模板的原因.
C语言并没有模板概念,这里借用C++的概念.
这种模板思想,在C语言其他地方也能使用. feichanglihai
页:
[1]