1. Buzzer例程控制原理
由ARM1138原理图可得:Buzzer使用CCP3(对应GPIO-G组0x40026000,4号引脚0x00000010)作为输入引脚;
Buzzer例程结构:
涉及的库函数使用绿色粗体表示
SysCtlPeripheralEnable(KEY_PERIPH); 当多个管脚处于同一个端口时,使能单个端口(设备的基地址 SYSCTL_PERIPH_I2C1 、 SYSCTL_PERIPH_PWM 、 SYSCTL_PERIPH_QEI0)
SysCtlPeripheralDisable(KEY_PERIPH); 禁止一个外设(设备的基地址)
GPIOPinTypeGPIOInput(KEY_PORT , KEY_PIN); 设置引脚为输入状态(端口基地址+偏移地址)
GPIOPinRead(KEY_PORT , KEY_PIN); 读取指定管脚上出现的值(端口基地址+偏移地址)
下面以SysCtlPeripheralDisable(unsigned long ulPeripheral);分析一下函数工作原理(寄存器地址映射原理):
核心的宏:HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) &= ~SYSCTL_PERIPH_MASK(ulPeripheral)
我们以本例中KEY_PERIPH(实际上是SYSCTL_PERIPH_GPIOG)为参数进行说明:如下可见它的值为0x20000040,
等式的左边:
1) 经过第一个宏处理取得上值的高四bits即0x2; (其实是将内存分为8个256M的内存块来管理,看目标寄存器是处于哪一个内存块中);
2) 经过第二个宏处理取得值 SYSCTL_RCGC2( 0x400FE108 列出的偏移量是相对于0x400F.E000的系统控制基址的寄存器地址的十六进制增量。 )即;通过RCGC2寄存器对目标设备进行控制;(每个内存块对应其自己的配置寄存器)
3) 经过第三个宏(*((volatile unsigned long *)(x))),即指向了寄存器RCGC2的空间。
等式右边:~(((0x20000040) & 0xffff) << (((0x20000040) & 0x001f0000) >> 16)) = 0xffffffbf (有效的是bf -> 10111111)
为了更好理解右式引入Enable的核心宏表达式
HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |= SYSCTL_PERIPH_MASK(ulPeripheral);
(((0x20000040) & 0xffff) << (((0x20000040) & 0x001f0000) >> 16)) = 0x00000040 (有效位40-> 01000000)
让我想起了常用的“&=”(某位置0,其余位不变) 还有 “|=”(某位置1,其余位不变)
综上:disable相应外设即是通过配置运行模式下的寄存器RCGC2。同时得 0x20000040 前1字节是为了分块,后2字节是为了配置寄存器的值。
不过前第二字节是为了什么?关键是弄明白这句的意思(((0x20000040) & 0x001f0000) >> 16)?
然后通过查阅《lm3s1138》的数据手册中对RCGC2寄存器的描述,即可明白整个函数的控制过程。
void SysCtlPeripheralDisable(unsigned long ulPeripheral)
{
//
// Check the arguments.
//
ASSERT((ulPeripheral == SYSCTL_PERIPH_ADC) ||
(ulPeripheral == SYSCTL_PERIPH_CAN0) ||
(ulPeripheral == SYSCTL_PERIPH_CAN1) ||
(ulPeripheral == SYSCTL_PERIPH_CAN2) ||
(ulPeripheral == SYSCTL_PERIPH_COMP0) ||
(ulPeripheral == SYSCTL_PERIPH_COMP1) ||
(ulPeripheral == SYSCTL_PERIPH_COMP2) ||
(ulPeripheral == SYSCTL_PERIPH_ETH) ||
(ulPeripheral == SYSCTL_PERIPH_GPIOA) ||
(ulPeripheral == SYSCTL_PERIPH_GPIOB) ||
(ulPeripheral == SYSCTL_PERIPH_GPIOC) ||
(ulPeripheral == SYSCTL_PERIPH_GPIOD) ||
(ulPeripheral == SYSCTL_PERIPH_GPIOE) ||
(ulPeripheral == SYSCTL_PERIPH_GPIOF) ||
(ulPeripheral == SYSCTL_PERIPH_GPIOG) ||
(ulPeripheral == SYSCTL_PERIPH_GPIOH) ||
(ulPeripheral == SYSCTL_PERIPH_HIBERNATE) ||
(ulPeripheral == SYSCTL_PERIPH_I2C0) ||
(ulPeripheral == SYSCTL_PERIPH_I2C1) ||
(ulPeripheral == SYSCTL_PERIPH_PWM) ||
(ulPeripheral == SYSCTL_PERIPH_QEI0) ||
(ulPeripheral == SYSCTL_PERIPH_QEI1) ||
(ulPeripheral == SYSCTL_PERIPH_SSI0) ||
(ulPeripheral == SYSCTL_PERIPH_SSI1) ||
(ulPeripheral == SYSCTL_PERIPH_TIMER0) ||
(ulPeripheral == SYSCTL_PERIPH_TIMER1) ||
(ulPeripheral == SYSCTL_PERIPH_TIMER2) ||
(ulPeripheral == SYSCTL_PERIPH_TIMER3) ||
(ulPeripheral == SYSCTL_PERIPH_UART0) ||
(ulPeripheral == SYSCTL_PERIPH_UART1) ||
(ulPeripheral == SYSCTL_PERIPH_UART2) ||
(ulPeripheral == SYSCTL_PERIPH_UDMA) ||
(ulPeripheral == SYSCTL_PERIPH_USB0) ||
(ulPeripheral == SYSCTL_PERIPH_WDOG));
//
// Disable this peripheral.
//
HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) &=
~SYSCTL_PERIPH_MASK(ulPeripheral);
}
/**************************************************************/
//***********************************************
//
// This macro extracts the array index out of the peripheral number.
//
//***********************************************
#define SYSCTL_PERIPH_INDEX(a) (((a) >> 28) & 0xf)
/**************************************************************/
static const unsigned long g_pulRCGCRegs[] =
{
SYSCTL_RCGC0,
SYSCTL_RCGC1,
SYSCTL_RCGC2
};
/**************************************************************/
#define HWREG(x) \
(*((volatile unsigned long *)(x)))
......
/**************************************************************/
#define SYSCTL_PERIPH_MASK(a) (((a) & 0xffff) << (((a) & 0x001f0000) >> 16))
/**************************************************************/
#define SYSCTL_PERIPH_WDOG 0x00000008 // Watchdog
#define SYSCTL_PERIPH_HIBERNATE 0x00000040 // Hibernation module
#define SYSCTL_PERIPH_ADC 0x00100001 // ADC
#define SYSCTL_PERIPH_PWM 0x00100010 // PWM
#define SYSCTL_PERIPH_CAN0 0x00100100 // CAN 0
#define SYSCTL_PERIPH_CAN1 0x00100200 // CAN 1
#define SYSCTL_PERIPH_CAN2 0x00100400 // CAN 2
#define SYSCTL_PERIPH_UART0 0x10000001 // UART 0
#define SYSCTL_PERIPH_UART1 0x10000002 // UART 1
#define SYSCTL_PERIPH_UART2 0x10000004 // UART 2
#ifndef DEPRECATED
#define SYSCTL_PERIPH_SSI 0x10000010 // SSI
#endif
#define SYSCTL_PERIPH_SSI0 0x10000010 // SSI 0
#define SYSCTL_PERIPH_SSI1 0x10000020 // SSI 1
#ifndef DEPRECATED
#define SYSCTL_PERIPH_QEI 0x10000100 // QEI
#endif
#define SYSCTL_PERIPH_QEI0 0x10000100 // QEI 0
#define SYSCTL_PERIPH_QEI1 0x10000200 // QEI 1
#ifndef DEPRECATED
#define SYSCTL_PERIPH_I2C 0x10001000 // I2C
#endif
#define SYSCTL_PERIPH_I2C0 0x10001000 // I2C 0
#define SYSCTL_PERIPH_I2C1 0x10004000 // I2C 1
#define SYSCTL_PERIPH_TIMER0 0x10100001 // Timer 0
#define SYSCTL_PERIPH_TIMER1 0x10100002 // Timer 1
#define SYSCTL_PERIPH_TIMER2 0x10100004 // Timer 2
#define SYSCTL_PERIPH_TIMER3 0x10100008 // Timer 3
#define SYSCTL_PERIPH_COMP0 0x10100100 // Analog comparator 0
#define SYSCTL_PERIPH_COMP1 0x10100200 // Analog comparator 1
#define SYSCTL_PERIPH_COMP2 0x10100400 // Analog comparator 2
#define SYSCTL_PERIPH_GPIOA 0x20000001 // GPIO A
#define SYSCTL_PERIPH_GPIOB 0x20000002 // GPIO B
#define SYSCTL_PERIPH_GPIOC 0x20000004 // GPIO C
#define SYSCTL_PERIPH_GPIOD 0x20000008 // GPIO D
#define SYSCTL_PERIPH_GPIOE 0x20000010 // GPIO E
#define SYSCTL_PERIPH_GPIOF 0x20000020 // GPIO F
#define SYSCTL_PERIPH_GPIOG 0x20000040 // GPIO G
#define SYSCTL_PERIPH_GPIOH 0x20000080 // GPIO H
#define SYSCTL_PERIPH_UDMA 0x20002000 // uDMA
#define SYSCTL_PERIPH_USB0 0x20100001 // USB0
#define SYSCTL_PERIPH_ETH 0x20105000 // ETH
#define SYSCTL_PERIPH_IEEE1588 0x20100100 // IEEE1588
#define SYSCTL_PERIPH_PLL 0x30000010 // PLL
#define SYSCTL_PERIPH_TEMP 0x30000020 // Temperature sensor
#define SYSCTL_PERIPH_MPU 0x30000080 // Cortex M3 MPU
(1)JTAG_Wait(); //防止JTAG失效;按住KEY2然后复位,程序会进入死循环。
防止JTAG失效这句话真是太含蓄了(很烦这个词,刚开始被说得云里雾里的),其实就是防止JTAG管脚被复用为GPIO管脚时在某些特定情况(详见手册)下被锁死(无法下载程序)的情况。关于防锁死程序原理:http://blog.163.com/liyupeng_china/blog/static/18464392820125199343114/
注意:由于所有的位都在复位时都会清零,因此在默认的情况下,这些GPIO线路设置GPIO模式。所以为保险起见,应加入下面的代码。以等待速度较慢的JTAG先对相应的GPIO复用脚设置为JTAG的模式。
void JTAG_Wait(void)
{
SysCtlPeripheralEnable(KEY_PERIPH); //使能KEY所在的GPIO端口,从刚才的分析即是使RCGC2 GPIOG位置1
GPIOPinTypeGPIOInput(KEY_PORT , KEY_PIN); // 设置KEY所在管脚为输入
if ( GPIOPinRead(KEY_PORT , KEY_PIN) == 0x00 ) // 如果复位时按下KEY,则进入
{
for (;;); // 死循环,以等待JTAG连接
}
SysCtlPeripheralDisable(KEY_PERIPH); // 禁止KEY所在的GPIO端口,使RCGC2 GPIOG位置0
}
(2)SystemInit();
下面分析一下下面的初始化函数都干了什么事(前3个函数)。
1.SysCtlLDOSet(); 控制LDO
LDO的作用:查了好些资料,也没弄明白LDO在1138中的具体作用,如果只是稳压,那么输出2.5V电压是做什么的?
2.SysCtlClockSet(); 设置系统时钟
ulConfig 参数是几个不同值的逻辑或,这些值中的某些值组合成组,其中只有一组值能被选用。
1)系统时钟分频器:SYSCTL_SYSDIV_1、SYSCTL_ SYSDIV_2、SYSCTL_SYSDIV_3 、 …、SYSCTL_SYSDIV_64 (在 Sandstorm-class 器 件 中 , 只 有SYSCTL_SYSDIV_1 到 SYSCTL_SYSDIV_16 是有效的。)
2)外部晶体频率:SYSCTL_XTAL_1MHZ 、SYSCTL_XTAL_1_84MHZ 、 SYSCTL_XTAL_2MHZ 、 SYSCTL_XTAL_2_45MHZ 、SYSCTL_XTAL_3_57MHZ 、 SYSCTL_XTAL_3_68MH 、 SYSCTL_XTAL_4MHZ 、SYSCTL_XTAL_4_09MHZ 、 SYSCTL_XTAL_4_91MHZ 、 SYSCTL_XTAL_5MHZ 、SYSCTL_XTAL_5_12MHZ 、 SYSCTL_XTAL_6MHZ 、 SYSCTL_XTAL_6_14MHZ 、SYSCTL_XTAL_7_37MHZ 、 SYSCTL_XTAL_8MHZ 、 SYSCTL_XTAL_8_19MHZ 、SYSCTL_XTAL_10MHZ 、 SYSCTL_XTAL_12MHZ 、 SYSCTL_XTAL_12_2MHZ 、SYSCTL_XTAL_13_5MHZ 、 SYSCTL_XTAL_14_3MHZ 、 SYSCTL_XTAL_16MHZ 或SYSCTL_XTAL_16_3MHZ (低于 SYSCTL_XTAL_3_57MHZ 的值在 PLL 被操作时无效。在Sandstorm-class 和 Fury-class 器件中,高于 SYSCTL_XTAL_8_19MHZ 的值是无效的。)
3)振荡器源:SYSCTL_OSC_MAIN 、 SYSCTL_OSC_INT 、SYSCTL_OSC_INT4、SYSCTL_OSC_INT30 或 SYSCTL_OSC_EXT32 (在 Standstorm-class器件中,SYSCTL_OSC_INT30 和 SYSCTL_OSC_EXT32 是无效的。SYSCTL_OSC_EXT32只可用于具有休眠模式的器件,并只在休眠模块已被使能时有效。)
使用 SYSCTL_USE_OSC | SYSCTL_OSC_MAIN 来选择由主振荡器提 供 系 统 时 钟 。 为 了 使 系 统 时 钟 由 PLL 来 提 供 , 请 使 用 SYSCTL_USE_PLL |SYSCTL_OSC_MAIN,并根据 SYSCTL_XTAL_xxx 值选择合适的晶体。
3.SysCtlClockGet(); 获取系统时钟
void SystemInit(void)
{
SysCtlLDOSet(SYSCTL_LDO_2_50V); // 设置LDO输出电压
SysCtlClockSet(SYSCTL_USE_OSC | // 系统时钟设置,采用主振荡器
SYSCTL_OSC_MAIN |
SYSCTL_XTAL_6MHZ |
SYSCTL_SYSDIV_1);
TheSysClock = SysCtlClockGet(); // 获取系统时钟,单位:Hz
BuzzerInit(); // 蜂鸣器初始化
}
void BuzzerInit(void)
{
SysCtlPeriEnable(CCP3_PERIPH); // 使能CCP3所在的GPIO端口(GPIOG)
GPIOPinTypeTimer(CCP3_PORT , CCP3_PIN); // 配置CCP3所在管脚(G4)为Timer功能(定时器1 TimerB)
SysCtlPeriEnable(SYSCTL_PERIPH_TIMER1); // 使能TIMER1模块
TimerConfigure(TIMER1_BASE , // 配置Timer1B为16位PWM
TIMER_CFG_16_BIT_PAIR | TIMER_CFG_B_PWM);
}
1138芯片资料有关定时器的设置过程:
(3)具体功能实现