Chinaunix首页 | 论坛 | 博客
  • 博客访问: 276397
  • 博文数量: 42
  • 博客积分: 590
  • 博客等级: 中士
  • 技术积分: 447
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-04 01:15
个人简介

健康快乐 虚怀若谷 淡定从容

文章分类

全部博文(42)

分类: 嵌入式

2012-09-12 15:52:38

说明:定义MCU头文件的通用方法。
参考:ST(意法半导体)的STM32库内提供的头文件。
日期:2012/09/12   lin

step1.  先分类寄存器,比如定时器、GPIO口、串口等; 针对每类寄存器用同样的结构体去实现它的内部项目,然后定义结构体指针,指向对应寄存器的首地址即可。
        这样定义的好处是:一个结构体代表一类寄存器;单纯定义结构体以及结构体指针变量均不会占用内存,编译器不会对其分配任何地址,即完全不会占用内存;给出一个初始地址,
                          结构体内部可自动算出各个变量(即内部的小寄存器)的地址。

        MCU中的存储模式很重要,所以要先了解一下大端模式与小端模式,否则会把结构弄反; 内存是“由前到后即低地址在前、高地址在后”排列的;
        小端模式就是数据的低字节存储在前(低地址),高字节存储在后(高地址);反之,大端模式就是数据的高字节存储在前(低地址),低字节存储在后(高地址)。

例如, 选择“8位且小端模式的MCU”为例。(16位、32位、64位机都类似)

8位的GPIO口:
typedef struct{
volatile unsigned char GPIO;
}_GPIO;

16位的定时器TIMER(8位机的16位寄存器一般都是由两个8位的寄存器构成,假设二者地址是连续,则就可定义到一个结构体中,如果不连续,则需分别定义。下面例子是连续的):
typedef struct{
volatile unsigned char TIMER_L;    //TIMER定时器的低位寄存器
volatile unsigned char TIMER_H;    //TIMER定时器的高位寄存器
}_TIMER;

结构体说明: 
                   
                   (1) 结构体内部变量及类型
                       第一、内部变量即为该寄存器内部包含的分寄存器;第二、必须是volatile类型,这种类型不会被编译器优化,它的意思是该变量的值随时都可能会变化,这种变化
                       不一定是代码中操作它才变的,也可能是内部自动变化,比如定时器,一旦打开之后,内部的计数寄存器的值就会一直增。

                   (2) 结构体对齐
                       第一、结构体内部每个变量类型都必须与实际寄存器所占的内存宽度一致; 第二、拼在一起时,变量之间不可出现空隙,一旦出现空隙,则后面的变量就跟实际的
                       地址不对应了。

                   (3) 结构体内部变量的排布
                       结构体定义方式就是根据一个初始地址,即可推算出结构体内部各个变量的地址,所以内部变量在排布时,必须跟手册中规定的实际地址对应。


step2.  定义寄存器的绝对基地址(或 某一块功能的绝对基地址以及它所包含的各个寄存器的偏移地址)
        例如, 
#define GPIO_BASE_ADD      (0x01)                  //GPIO的绝对基地址
        #define GPIOA_BASE_ADD     (GPIO_BASE_ADD)       // GPIOA的绝对基地址
        #define GPIOB_BASE_ADD     (GPIO_BASE_ADD+0x01)    // GPIOB的绝对基地址
        #define GPIOB_BASE_ADD     (GPIO_BASE_ADD+0x02)    // GPIOC的绝对基地址

#define TIMER_BASE_ADD     (0x06)                  // TIMER的绝对基地址
#define TIMER0_BASE_ADD     (TIMER_BASE_ADD)       // TIMER0的绝对基地址
#define TIMER1_BASE_ADD     (TIMER_BASE_ADD+0x02)  // TIMER1的绝对基地址
#define TIMER2_BASE_ADD     (TIMER_BASE_ADD+0x04)  // TIMER2的绝对基地址

step3.  定义寄存器指针

#define GPIOA              ((_GPIO*)GPIOA_BASE_ADD)
#define GPIOB              ((_GPIO*)GPIOB_BASE_ADD)
#define GPIOC              ((_GPIO*)GPIOC_BASE_ADD)
#define TIMER0             ((_TIMER*)TIMER0_BASE_ADD)
#define TIMER1             ((_TIMER*)TIMER1_BASE_ADD)
#define TIMER2             ((_TIMER*)TIMER2_BASE_ADD)

step4.  调用方法

       GPIOA、GPIOB、GPIOC、TIMER0、TIMER1、TIMER2都属于指针类型,所以在调用时,要注意一点。例如,对GPIOA赋值时,GPIOA->GPIO = 0xff;

       定义函数时,假如是需要使用它们作为形参,必须定义成对应的指针类型,例如:
       void SET_GPIO(_GPIO* GPIO_X)
       {
GPIO_X->GPIO = 0xff;
}

       unsigned short Read_TIMER(_TIMER* TIMER_X)
       {
unsigned short t;
t = TIMER_X->TIMER_H;
                t = (t<<8) | TIMER_X->TIMER_L;
return t;
}

总结: 对于操作MCU,实际就是根据MCU的数据手册,操作各种寄存器,本质上就是操作寄存器所代表的内存地址。
阅读(2466) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~