非操作系统下GPIO口控制器及LED灯编程
GPIO控制器
概述
S3C6410 共有 187 根 IO 口,分成 17 个 PORT,每根 IO 口可以被用在输入引脚线、输出引脚线和其它功能引脚线使用,可以通过每个端口自带的控制寄存器进行功能配置。
‘
内部架构
- GPIO控制器挂在APB总线,属于低速设备。
- 控制器包括寄存器文件和相应的功能电路组成,通过寄存器可以控制功能电路按照用户的方式进行工作。
- 整个控制器包括 alive-part , off-part 两个部分, 区别在于sleep mode 时是否有电源供应,在 alive-part 时有电源供给,所以其端口可用作 wake –up 唤醒信号
端口寄存器
GPIO控制寄存器
- 功能 – 配置相应端口的功能
GPIO数据寄存器
- 数据寄存器每个bit位对应相应端口的IO线
- 如果该IO线被用做输入口引脚,该bit位随着引脚线电平的变化而变化(其对应值为0或者1);
- 如果该IO线被用作输出口引脚,则该bit位值为1时,外面对应引脚线为高电平(一般为3.3v左右),则该bit位值为0时,外面对应引脚线为低电平(一般为略大于0V)
GPIO上拉寄存器
- 该寄存器可以对 arm 一些特殊io做上拉或下拉处理,这在一些场合是非常有用的,避免了在pcb上加上拉或下拉电阻,能够简化外部电路设计
非操作系统LED灯闪烁
开发步骤
- 看懂硬件图
- 编写驱动
- 编写应用程序
- 使用工具
- 系统调试
开发板上二极管
读电路图
S3C6410芯片手册
程序框架
- 汇编
;asses.s内容
AREA Init, CODE READONLY
ENTRY
_start
B Main
END
- 编写应用程序
// ledapp.c
void main()
{
ledconfig();
for(…)
{
ledon();delay();
ledoff();delay();
}
}
- 编写驱动
// leddrv.c
void ledconfig()
{
……
}
void ledon()
{
……
}
void ledoff()
{
……
}
驱动编写示例 leddrv.c
- 配置M口M1引脚为输出口
#define rGPMCON *((volatile int *) 0x7f008820 )
void ledconfig()
tmp= rGPMCON; //读出端口寄存器值
tmp &=! (0xF<<4); //把bit4~7清0
tmp |= (1<<4); //把bit4~7写入0x1值
rGPMCON = tmp; //写回到端口寄存器
}
- 点亮
配置M口M1输出低电平
#define rGPMDAT *((volatile int *) 0x7f008824 )
void ledon()
{
tmp= rGPMDAT; //读出端口寄存器值
tmp &=! (0x1<<1); //把bit1清0
tmp |= (0<<1); //把bit1写入0x0值
rGPMDAT = tmp; //写回到端口寄存器
}
- 熄灭
配置M口M1输出高电平
void ledoff()
{
tmp= rGPMDAT; //读出端口寄存器值
tmp &= ! (0x1<<1); //把bit1清0
tmp |= (1<<1); //把bit1写入0x1值
rGPMDAT = tmp; //写回到端口寄存器
}
课后作业
编写一个在非操作系统LED灯闪烁的程序,要求
1)控制GPM2和GPM4端口;
2)写出驱动程序和应用程序。
刚开始嵌入式,对于硬件方面有很多不理解,不过这不影响我编程。
答案如下:
- 驱动程序 leddrv.c
// leddrv.c
#define rGPMCON *((volatile int *) 0x7f008820 )
// 控制GPM2和GPM4端口
void ledconfig(){
tmp= rGPMCON; //读出端口寄存器值
tmp &= !(0xF<<8); //把bit8~11清0
tmp |= (1<<8); //把bit8~11写入0x1值
tmp &= !(0xF<<16); //把bit16~19清0
tmp |= (1<<16); //把bit16~19写入0x1值
// 这里再提供一种简便写法,完全等价上面四行
// tmp &= !(0xF<<8 | 0xF<<16);
// tmp |= (1<<8 | 1 <<16);
// 这里再再提供一种写法
// tmp &= !(0xF0F << 8);
// tmp |= (0x101 << 8);
rGPMCON = tmp; //写回到端口寄存器
}
#define rGPMDAT *((volatile int *) 0x7f008824 )
void ledon()
{
tmp= rGPMDAT; //读出端口寄存器值
tmp &= !(0x1<<2); //把bit2清0
tmp |= (0<<2); //把bit2写入0x0值
tmp &= !(0x1<<4); //把bit4清0
tmp |= (0<<4); //把bit4写入0x0值
// 这里再提供一种简便写法,完全等价上面四行
// tmp &= !(0x1<<2 | 0x1<<4);
// tmp |= (0<<2 | 0<<4)
rGPMDAT = tmp; //写回到端口寄存器
}
void ledoff()
{
tmp= rGPMDAT; //读出端口寄存器值
tmp &= !(0x1<<2); //把bit2清0
tmp |= (1<<2); //把bit2写入0x1值
tmp &= !(0x1<<4); //把bit4清0
tmp |= (1<<4); //把bit4写入0x1值
// 这里再提供一种简便写法,完全等价上面四行
// tmp &= !(0x1<<2 | 0x1<<4);
// tmp |= (1<<2 | 1<<4)
// 还可以这么写
// tmp &= !(0x5 << 2);
// tmp |= (0x5 << 2);// bit2,bit4写入1,相当于101左移2位
rGPMDAT = tmp; //写回到端口寄存器
}
- 应用程序 ledapp.c
// leadapp.c
void delay(){ // 停止一段时间
for( i=0;i<1000;i++)
for( j=0;j<1000;j++);
}
void main()
{
ledconfig(); // 配置驱动
while(1)
{
ledon();delay();
ledoff();delay();
}
}
配套作业
(1) S3C6410 处理器其内部知识产权核使用(ARM11),该处理器内置(看门狗控制器、GPIO口控制器)等多接口控制器。
(2) 接口编程核心是要掌握端口寄存器,一般情况下程序员通过(芯片手册)来了解端口寄存器定义和使用方法。假设某端口寄存器地址为0xC2000004,32比特大小,需要将其第8-9位写入二进制值(10)2,而其它位保持不变,请写出关键代码(采用内存指针法)
tmp = *((volatile int*)0xC2000004);
tmp &= !( 0x3 << 8);
tmp |= (0x3 << 8);
*((volatile int*)0xC2000004) = tmp;
(3) 看门狗部件属于(硬件模块),其核心功能为(侦测软件代码跑飞,当系统“跑飞”而进入死循环时,恢复系统的运行)。