可知,AHB_BASE + 0X00200指的是CLK_BA 时间控制寄存器
而SYSCLK_T的结构体中定义的成员正是上表中的寄存器。所以SYSCLK->PWRCON 就是指 系统掉电控制寄存器 。我们再考虑PWRCON的数据类型。
由此,我们看到,PWRCON也是一个结构体类型的变量,其结构体为SYSCLK_PWRCON_T。看到这里,我们终于明白SYSCLK->PWRCON.XTL12M_EN = 1;是将 结构体类型SYSCLK中的结构体PWRCON中的变量XTL12M_EN赋值为1。接下来需要弄清楚的是XTL12M_EN 是什么东西,查手册可知
最后,费了这么大的劲,原来SYSCLK->PWRCON.XTL12M_EN = 1; 是在设置 系统使用外部晶振。
3、LOCKREG();
显然此函数是 锁定寄存器写保护,不至于随意改变寄存器的值。
给寄存器写保护控制寄存器(0x5000_0000+0x00000+0x100)写入任意数,即可进入保护状态。此处写的是0x00 。
4、DrvGPIO_Open(E_GPA,2, E_IO_OUTPUT);
我们打开驱动参考手册,在里面找到:
的说明。我们可以看到,详细的函数说明信息,即此函数的功能是:设置端口的输出模式。那我们接下来看这个函数的源代码:
int32_t DrvGPIO_Open(E_DRVGPIO_PORT port, int32_t i32Bit, E_DRVGPIO_IO mode)
{
volatile uint32_t u32Reg;
if ((i32Bit < 0) || (i32Bit > 16))
{
return E_DRVGPIO_ARGUMENT;
}
u32Reg = (uint32_t)&GPIOA->PMD + (port*PORT_OFFSET);
if ((mode == E_IO_INPUT) || (mode == E_IO_OUTPUT) || (mode == E_IO_OPENDRAIN))
{
outpw(u32Reg, inpw(u32Reg) & ~(0x3<<(i32Bit*2)));
if (mode == E_IO_OUTPUT)
{
outpw(u32Reg, inpw(u32Reg) | (0x1<<(i32Bit*2)));
}else
if (mode == E_IO_OPENDRAIN)
{
outpw(u32Reg, inpw(u32Reg) | (0x2<<(i32Bit*2)));
}
}else
if (mode == E_IO_QUASI)
{
outpw(u32Reg, inpw(u32Reg) | (0x3<<(i32Bit*2)));
}else
{
return E_DRVGPIO_ARGUMENT;
}
return E_SUCCESS;
}
我们先逐条看里面内容。
E_DRVGPIO_PORT: E_DRVGPIO_IO:
定义了两个枚举类型,前面枚举出可能的端口号(A,B,C,D,E),后一个枚举出端口的输出模式(输入,输出,开漏,准双向)。这两个在加上uint32_t 一共在函数中定义了三个局部变量。
该句是设定 局部变量范围。必须规定在0~16之间。因为函数所定义的第二个局部变量i32bit 是控制移位的,后面将会看到为什么定义这个范围。
此句显然是将某个具体的地址值赋给变量u32Reg,那么此地址是
地址为AHB_BASE+0x4000所指的寄存器,查数据手册可知
(uint32_t)&GPIOA->PMD指的是GPIO端口A 的PMD寄存器位。而port*PORT_OFFSET(此处offset是宏定义,指的是数0x40)就是使地址能够指向不同的I/O端口。由此u32Reg = (uint32_t)&GPIOA->PMD + (port*PORT_OFFSET);的意思就是某个端口(A,B,C,D,E)的PMD地址 = A 端口的PMD地址 + 端口号*40H。
此句是说明定义的输出模式如果不是准双向,则执行函数outpw(u32Reg, inpw(u32Reg) & ~(0x3<<(i32Bit*2))); 那么函数outpw(port,value)又是什么呢?
说明是给 某个地址 赋给 值value。因此,此句的意思是给地址为u32reg的端口寄存器赋value。
(0x3<<(i32Bit*2))即使第i32bit位赋值 11,前面加上取反即使给第i32bit位清零。
说了这么多,全是为这一句话——outpw(u32Reg, inpw(u32Reg) & ~(0x3<<(i32Bit*2)));。即将端口u32Reg的第i32Bit位赋值00 ,其余位的值保持不变(因为是还读了一下u32Reg的值呢,inpw函数)。
太麻烦啦说得这么细,是在太累啦~休息一下……不过这点讲清楚了下面就容易啦……
下来就一样啦,设置成输出模式,就给端口的第 i32Bit位赋01,输入模式就赋10 。
5、DrvGPIO_ClrBit(E_GPA,2);
下面进入主循环。
可以看出,tGPIO是一个GPIO_T型的结构体,函数的关键在于
由上面可知此句是端口的地址赋给指针tGPIO(这里要注意,是结构体类型的指针,里面数据占得空间大小要小心)。
有了前面的经验很容易看出,这里是要给端口的引脚输出值(置0)。
所以,此函数的意思是将E_GPA端口的第二引脚置0 。
6、DrvGPIO_SetBit(E_GPA,2);
和上面一样,是将E_GPA端口的第二引脚置1 。
总结:写到这里,我们终于可以看到这个主函数究竟是要做些什么工作啦。从原理图上可知A端口的2,3,4,5分别连接LED灯。所以,程序结果是:四个灯依次亮,之后全灭,往复循环。
本例程虽然只是一个相当简单的Cortex程序,我们完全可以只看用户端的函数驱动来进行编写,但是当我们真正将一些底层驱动函数研究之后,相信我们对Cortex掌握理解得更加彻底。等我们完全熟悉之后,再凭借我们的经验进行基于用户函数的开发,这样相对来说要好得多。
Watch186
2011年11月 |