到板子之后两天,终于可以下载程序了。
首先的学习我就从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的每一位的对应关系。
#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与汇编混合编程的习惯!