现在的位置: 首页 > 综合 > 正文

初学56F8366之GPIO口操作

2013年12月11日 ⁄ 综合 ⁄ 共 4355字 ⁄ 字号 评论关闭

到板子之后两天,终于可以下载程序了。

      首先的学习我就从IO口操作看起了,看了一些LQ的例程。这个IO口的操作和单片机的IO口操作不同,因为头文件不健全,目前只能使用汇编去访问寄存器。

      asm(bfclr #0x7FF,X:GPIO_A_PER);  

      asm(bfset #0x7FF,X:GPIO_A_DDR);

      目前只能通过以上方式去访问寄存器。asm(),为标示括号内为汇编内容。而格式为#<表达式>表示为立即数,X:表示基址+变址寻址。而bfclr被称为16位屏蔽逻辑,仅有为1的位被处理,而为0的位被忽略。就语句asm(bfset #0x7FF,X:GPIO_A_DDR);而言,其意义为使得通用IO口的方向寄存器0至11位置位,而其他则不变。转换为C语言相当于 GPIO_A_DDR |= 0x07FF;

      总而言之,bfclr相当于&=~,bfset相当于|=,cfchg相当于^=;

      这样,就可以开始单片机最基本的程序点亮一个LED了。

      假设我GPIO口A1挂接好了一个LED,其点亮他的程序为。

      asm(bfclr  #0x0002,X:GPIO_A_PER);     // 开启为GPIO
      asm(bfset #0x0002,X:GPIO_A_PUR);     // 输出上拉使能
      asm(bfset #0x0002,X:GPIO_A_DDR);     // 设置方向为输出
      for(;;)
      {
             asm(bfclr  #0x0002,X:GPIO_A_DR);       // 输出低电平 
             delay();                               // 延时
             asm(bfset  #0x0002,X:GPIO_A_DR);       // 输出低电平 
             delay();                               // 延时
      }

     这样,理应就会使得LED在不断的闪烁,但是结果却是错误的。追究其原因,是因为我错误的认为GPIOA寄存器的第2位就是对应着GPIO口的A1。实则并不是这样的。下图为寄存器GPIO_A_PER的每一位的对应关系。

初学56F8366之GPIO口操作 - wehat1148158886 - 子诺
       其实GPIO口的A口有17个,即A0-A16。但GPIOA的寄存器只是14位的,所以关系必定不是按顺序来的。从图中可以看出,A1挂了一个名称为LED19的LED,它位于GPIO寄存器的第9位。所以要操作A1口所写入的数值为0x0200,而并非0x0002。0x0002从上图可以看出是对于A9口进行操作。所以,要让LED19闪烁的程序如下所示。

     #include <stdio.h>
     #include <stdlib.h>
     #include "MC56F834X.h"

    void delay(int ms)
   {   
         int ii,jj;
         if (ms<1) ms=1;
         for(ii=0;ii<ms;ii++)
            for(jj=0;jj<2670;jj++);       
   }

   int main(void)
   {
        asm(bfclr #0x0200,X:GPIO_A_PER);   //开启GPIO
        asm(bfset #0x0200,X:GPIO_A_PUR);   //上拉电阻
        asm(bfset #0x0200,X:GPIO_A_DDR);   //方向为输出
   
        for(;;)
        {
             asm(bfclr #0x0200,X:GPIO_A_DR);
             delay(200);
             asm(bfset #0x0200,X:GPIO_A_DR);
             delay(200);
       }
  }

        事实上,即使到这里,也还有一些问题的存在。

       bfset指令的格式为 bfset #<立即数>,X:<变址寻址>。

       那这样就有很多的不便了,立即数是不能替换为变量的。这也就造成了编程非常的不便。至少我希望对于操作寄存器这边可以随心所欲的操作。对于寄存器的操作,只能是通过把变量MOV至一个寄存器,再由寄存器给传至相关寄存器。对于习惯了C我,这不是很方便。

       为了替换以上汇编的内容,我希望可以有一个C的头文件,直接可以对寄存器写值。拿寄存器GPIO_A_DR来说,其地址为0x0000F2E1,那么,要向这个寄存器写值,可以用语句:#define   GPIO_A_DR   (*((volatile word *)0x0000F2E1));来定义GPIO_A_DR。那么,就可以对GPIO_A_DR进行一般的操作了。我在PE模式下,找到了一个名为IO_Map.h与PE_Types.h的头文件,里面已经定义好了这些东西。只需要拷贝一下就能用。

       我的LQ的8366板子上,A15与A12口接上了2个按键,常态为1,按下为0。A1-A8上接了8个LED。以下代码为一个可变速的LED流水灯,算是最经典的入门程序了,我玩的第一块单片机也是这个程序。

#include <stdio.h>
#include <stdlib.h>
#include "IO_Map.h"

#define    LED10     0x0001
#define    LED11     0x0008     //E口
#define    LED12      0x0004     //E口
#define    LED15     0x2000  
#define    LED16     0x1000
#define     LED17        0x0800
#define     LED18        0x0400
#define     LED19        0x0200

#define     KEY_UP       0x0080    
#define     KEY_DOWN     0x0010

word LED[8] = {LED10,LED11,LED12,LED15,LED16,LED17,LED18,LED19};
 
   
void delay(int ms)
{  
    int ii,jj;
    if (ms<1) ms=1;
    for(ii=0;ii<ms;ii++)
      for(jj=0;jj<2670;jj++);       
}

int main(void)
{
    unsigned int Count = 0;
    unsigned int peed = 200;
    //8个LED初始化         
    GPIO_A_PER &= ~((word)0x3E01);    //开启GPIO
    GPIO_A_PUR |=   (word)0x3E01;       //上拉电阻
    GPIO_A_DDR |=   (word)0x3E01;       //方向为输出
    GPIO_A_DR  |=   (word)0x3E01;        //灭灯
   
    GPIO_E_PER &= ~((word)0x000C);    //开启GPIO
    GPIO_E_PUR |=   (word)0x000C;       //上拉电阻
    GPIO_E_DDR |=   (word)0x000C;       //方向为输出
    GPIO_E_DR  |=   (word)0x000C;        //灭灯
   
    //UP和DOWN两个按键初始化
    GPIO_A_PER &= ~((word)0x0090);    //开启GPIO
    GPIO_A_PUR |=   (word)0x0090;       //上拉电阻
    GPIO_A_DDR &= ~((word)0x0090);       //方向为输入  
   
    for(;;)
    {
        if(LED[Count] == LED11 || LED[Count] == LED12)
        {
         GPIO_E_DR  &=~  (word)LED[Count];           
            delay(peed);
         GPIO_E_DR  |=   (word)LED[Count];      
            delay(peed); 
        }
        else
        {
         GPIO_A_DR  &=~  (word)LED[Count];           
            delay(peed);
      GPIO_A_DR  |=   (word)LED[Count];      
         delay(peed);
        }
     Count++;
     if(Count >= 8) Count = 0;
     if((GPIO_A_DR & KEY_UP) ==0 || (GPIO_A_DR & KEY_DOWN) ==0)
     {
      delay(100);     //延时去抖动
      if((GPIO_A_DR & KEY_UP)   ==0)
      {
          if(peed > 0x0020)peed -= 20;   //加速
       while(1) if((GPIO_A_DR & KEY_UP) != 0) break;    //等待放开
          delay(100);     //延时去抖动
      }
      if((GPIO_A_DR & KEY_DOWN)   ==0)
      {
          if(peed < 0x0f00)peed += 20;    //减速
       while(1) if((GPIO_A_DR & KEY_DOWN) != 0) break;    //等待放开
          delay(100);     //延时去抖动
      }
     }        
    }
}

     以上程序,涉及了寄存器的读写,较汇编而言较为方便。原则上说,只要会读写寄存器了,那么就能任何的外设都不成问题了。但是,8366是DSP,只是嵌入了一个MCU,要是只用其来对外设进行操作,那可真是大材小用了! 另外,并不是汇编就真那么烂,汇编的执行效率较高,但是对于基本要求就很高了,在某些要求较高的算法上,该用汇编还是得用,最好形成一个C与汇编混合编程的习惯!

抱歉!评论已关闭.