① 设计一个硬件电路,使S3C2410的GPF5引脚控制一个LED灯,编写对应的对应的汇编代码,使LED灯不停闪烁,同时
同时什麼?
② 请帮我解释下这段s3c2410 嵌入式开发中的代码,是将文件从flash中复制到ram中的程序 #include "def.h" #in
#include "def.h"
#include "2410addr.h"
#include "2410lib.h"
#define ARM_ADDR 0x30000000; /*定义一个地址,应该是复位之后要跳转到的地址*/
void (*run)(void); /* 一个指针函数 */
void ARM9init(void);
void (CopeFromFlashToRM(U32 * FlashAddr.U32 *ArmAddr,U32 ul);
void (void);
void main(void)
{
run=(void(*)(void)) ARM_ADDR;/* 从ARM_ADDR开始执行,遇到return返回继续下面的函数 */
ARM9init(); /* 调用函数 */
();
run();
}
CopyFromFlashToRAM(U32 * pulFlashAddr,U32 *pulArmAddr,U32 ul)
/* 将长度为ul 的内容从pulFlashAddr 到 pulArmAddr */
{
U32 *pulSource=pulFlashAddr;/* 参数1的数值传递给变量 */
U32 *pulDest=pulArmAddr;
U32 i;
ul/=4; /* ul = ul/4,ul应该是要文件的长度 */
for(i=0;i<ul;i++) /* 重复执行指令 */
{
*pulDest++=*pulSource++; /* 源地址的内容到目的地址 */
}
} /* CopyFromFlashToRAM 函数结束 */
(void)
{
U32 *p1;
U32 *p2;
p1=(U32 * )0x00200000; /* flash 地址*/
p2=(U32 * )0x30000000; /* RAM 地址*/
CopyFromFlashRAM(p1,p2,0x20000); /* 将长度为0x20000 的内容从p1 到 p2 */
}
③ 如何利用s3c2410的gpio实现数据输入和输出
这些函数的实现在linux内核源代码的/arch/arm/plat_s3c24xx/gpio.c中实现,这些函数的具体内容就不在这里介绍了,可以通过源代码进行查看,这里对这些函数的用法进行解读:
1.void s3c2410_gpio_cfgpin(unsigned int pin,unsigned int function)
第一个参数pin 是对应的io引脚(这里用宏S3C2410_GPB(5),5不是固定的,看你需要引用的引脚而定)
第二个引脚是设置该引脚的功能的
(由S3C2410_GPIO_INPUT,S3C2410_GPIO_OUTPUT,S3C2410_GPIO_SFN2,S3C2410_GPIO_SFN3这4个宏进行定义)
例如:s3c2410_gpio_cfgpin(S3C2410_GPB(5),S3C2410_GPIO_INPUT)
设置GPB5引脚为输入。
2.unsigned int s3c2410_gpio_gecfg(unsigned int pin)
作用:返回对应的GPIO的配置情况
例如:pin=s3c2410_GPB5返回GPB5的配置情况
3.void s3c2410_gpio_pullup(unsigned int pin,unsigned int to)
作用:设置相应的的GPIO的上拉电阻。
第一个参数:相应的引脚,和1里面的用法一致。
第二个参数:设置为1或者0,1表示上拉,0表示不上拉。
4.void s3c2410_gpio_setpin(unsigned int pin,unsigned int to)
作用:将相应的引脚输出为1或者0。
第一个参数:相应的引脚宏
第二个参数:1或者0
例子:s3c2410_gpio_setpin(S3C2410_GPB(5),1)将引脚GPB5输出为1
5.unsigned int s3c2410_gpin_getpin(unsigned int pin)
功能:获取输入值
参数:相应的引脚
6.unsigned int s3c2410_modify_misccr(unsigned int clear ,unsigned int change)
7.int s3c2410_gpio_getirq(unsigned pin)
④ linux中可以直接访问S3C2410的ADC寄存器吗
哥们 我遇到了和你一样的问题 linux下是不可以直接访问物理寄存器的 需判培要用到虚拟地址 经
过一两天的摸索,基本上对在linux控制硬件有了个初步的认识,在linux下控制硬件和在无操作系统下控制硬件的不同主要在于硬件的地址不一样,在
linux下要使用va(虚拟地址),而在无操作系统下可以直接使用硬件的pa(物理地址),当然,在这里还需要进一步的摸索和求证,是不是在编译内核时
去掉某个模块后,可以在linux下直接使用pa,显然,我这两天的摸索是基于无法直接使用pa的。
在linux-2.6.8.1/include/asm-arm/arch-s3c2410/map.h中定义了大部分硬件的物理地址和他们的虚拟地址。
在光盘的用户手册上,um_s3c2410x_rev11_012003.pdf中也就是所谓的datasheet中,同样对硬件的物理地址进行了说明。
pa与va 不同在于基址,他们的偏移量是一样的。
现以gpio F为例说明,gpio 的pa 基址(ba)为配山0x56000000,GPFCON pa为0x56000050
即:可见偏移量为0x50,而我们在看看GPFCON va ,vaba :0xf0e0 0000,va:0xf0e0
0050,偏移量为0x50,现在,一切都变的明了起来了。我们只要知道了vaba,和他的偏移量,我们就能计算出va,从而,就可以对其进行操作了。这
样,也就有了两个问题,1:如何获取vaba:在linux-2.6.8.1/include/asm-arm/arch-s3c2410/map.h中
有定义,在这里我们还有必要对map.h的地址的定义进行一下的说明
计算机中,分级分层的思想随处可见,这也是计算机上的一个基本的思想和思路。在这里,也不例外。用培冲中俺老赵的话说就是分了三级,三级偏移,党中央的ba为
0xf0000000,偏移一下到省里,0xf0e0 0000 (以GPIO为例),再次偏移到基层,0xf0e0 0050
(以GPFCON为例)。哈哈,现在,就可以在linux 下通过0xf0e0 0050来对GPFCON 寄存器来进行操作了。
现把源码中的实现过程摘出如下:(当然,我们具体实现时,完全可以事先计算出va,过程如上)
#define S3C2410_ADDR(x) (0xF0000000+(x))//map.h
//linux下所有硬件的党中央vaba:0xF0000000
#define S3C2410_VA_GPIO S3C2410(0X00E00000)//map.h
//GPIO的偏移量0x00E00000,加上这个偏移量后,到了GPIO器件
#define S3C2410_GPIOREG(x) ((x)+S3C2410_VA_GPIO)
#define S3C2410_GPFCON S3C2410_GPIOREG(0x50)//regs-gpio.h
//GPFCON寄存器的偏移量0x50,加上这个偏移量后,到了具体的寄存器,可以对硬件进行操作了
#define S3C2410_GPFDAT S3C2410_GPIOREG(0x54)//regs-gpio.h
#define S3C2410_GPFUP S3C2410_GPIOREG(0x58)//regs-gpio.h
2:第二个问题就是偏移量的获取,其实这个问题已经在1中解决了。呵呵,去查具体硬件的头文件就行了。
⑤ 求助 s3c2410把nandflash中的程序搬到sdram中并运行的程序
你可以自己写一个汇编的程序,把Nand Flash 中的程序搬到SDRAM中。因为S3C2410有Nor Flash和Nand Flash有两种启动方式,所以在搬移过程中略有不同。如果用Nand Flash启动可以使用下面碰晌的代码,至于Nor Flash启动就相对简单了,你可以自己研究一下。
文件1.head.s
@ 文件 head.s
@ 作用:关闭看门狗、SDRAM 的初始化设置、搬移 Nand Flash 4K 以后
@ 的代码到 SDRAM 的指定位置、执行 SDRAM 中的代码
.text
.global _start
_start:
ldr r0, =0x53000000 @ Close Watch Dog Timer
mov r1, #0x0
str r1, [r0]
bl memory_setup @ Initialize memory setting
bl flash_to_sdram @ Copy code to sdram
ldr sp, =0x34000000 @ Set stack pointer
ldr pc, =main @ execute the code in SDRAM
文件2:flash.s
@ 文件 flash.s
@ 作用:设置 Nand Flash 的控制寄存器物吵余、读取 Nand Flash
@ 中的代码到 SDRAM 的指定位置
.equ NFCONF, 0x4e000000
.equ NFCMD, 0x4e000004
.equ NFADDR, 0x4e000008
.equ NFDATA, 0x4e00000c
.equ NFSTAT, 0x4e000010
.equ NFECC, 0x4e000014
.global flash_to_sdram
flash_to_sdram:
@ Save return addr
mov r10,lr
@ Initialize Nand Flash
mov r0,#NFCONF
ldr r1,=0xf830
str r1,[r0]
@ First reset and enable Nand Flash
ldr r1,[r0]
bic r1, r1, #0x800
str r1,[r0]
ldr r2,=NFCMD
mov r3,#0xff
str r3,[r2]
@ for delay
mov r3, #0x0a
1:
subs r3, r3, #1
bne 1b
@ Wait until Nand Flash bit0 is 1
wait_nfstat:
ldr r2,=NFSTAT
ldr r3,[r2]
tst r3,#0x01
beq wait_nfstat
@ Disable Nand Flash
ldr r0,=NFCONF
ldr r1,[r0]
orr r1,r1,#0x8000
str r1,[r0]
@ Initialzie stack
ldr sp,=4096
@ Set arguments and call
@ function nand_read defined in nand_read.c
ldr r0,=0x30000000
mov r1,#4096
mov r2,#1024
bl nand_read
@ return
mov pc,r10
文件3:interrupt.c
/罩滚*
* 文件 interrupt.c
* 作用:设置并响应按键中断
*/
#include "printf.h"
#define GPECON (*(volatile unsigned long *)0x56000040)
#define GPEDAT (*(volatile unsigned long *)0x56000044)
#define GPEUP (*(volatile unsigned long *)0x56000048)
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPFUP (*(volatile unsigned long *)0x56000058)
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
#define GPGUP (*(volatile unsigned long *)0x56000068)
#define EINTMASK (*(volatile unsigned long *)0x560000a4)
#define INTMSK (*(volatile unsigned long *)0X4a000008)
#define PRIORITY (*(volatile unsigned long *)0x4a00000c)
#define EINTPEND (*(volatile unsigned long *)0x560000a8)
#define INTPND (*(volatile unsigned long *)0X4a000010)
#define SRCPND (*(volatile unsigned long *)0X4a000000)
#define BIT_EINT0 (0x1 << 0)
#define BIT_EINT2 (0x1 << 2)
#define BIT_EINT8_23 (0x1 << 5)
#define SET_KEY_INTERRUPT_REG() ({ \
GPGCON = (GPGCON & (~((3<<12)|(3<<4)))) | ((1<<12)|(1<<4)) ; \
GPGDAT = GPGDAT & (~((1<<6)|(1<<2))); \
GPECON = (GPECON & (~((3<<26)|(3<<22)))) | ((1<<26)|(1<<22)); \
GPEDAT = GPEDAT & (~((1<<13)|(1<<11))); \
GPGCON = (GPGCON & (~((3<<22)|(3<<6)))) | ((2<<22)|(2<<6)) ; \
GPFCON = (GPFCON & (~((3<<4)|(3<<0)))) | ((2<<4)|(2<<0)) ; \
})
__inline void ClearPending(int bit)
{
SRCPND = bit;
INTPND = bit;
}
void init_irq( ) {
GPFCON = ((0x1<<8) | (0x1 << 10) | (0x1 << 12) | (0x1 << 14)); // Set the led D9~D12 output
/*
GPGCON = (GPGCON & (~((3<<12)|(3<<4)))) | ((1<<12)|(1<<4)) ; // GPGCON6,2 set output
// GPGCON6:KSCAN1
// GPGCON2:KSCAN3
GPGDAT = GPGDAT & (~((1<<6)|(1<<2))); // GPGDAT6,2 output 0
GPECON = (GPECON & (~((3<<26)|(3<<22)))) | ((1<<26)|(1<<22)); // GPECON13,11 set output
GPEDAT = GPEDAT & (~((1<<13)|(1<<11))); // GPEDAT13,11 output 0
GPGCON = (GPGCON & (~((3<<22)|(3<<6)))) | ((2<<22)|(2<<6)) ; // GPGCON11,3 set EINT
GPFCON = (GPFCON & (~((3<<4)|(3<<0)))) | ((2<<4)|(2<<0)) ; // GPFDAT2,0 set EINT
*/
// Use the defined micro instead of above code
SET_KEY_INTERRUPT_REG();
GPFUP |= (1<<0) | (1<<2); // Up
GPGUP |= (1<<3) | (1<<11); // Up
EINTPEND |= (1 << 19) | (1 << 11); // Clear eint 11,19
EINTMASK &= (~((1 << 19) | (1 << 11))); // Enable EINT11,19
ClearPending(BIT_EINT0|BIT_EINT2|BIT_EINT8_23); // Enable EINT0,2 and the EINT8_23
INTMSK &= (~0x25);
return;
}
int Key_Scan( void )
{
int i;
for(i = 0; i < 1000 ;i++) ;
GPGDAT = (GPGDAT &(~((1<<6)|(1<<2)))) | (1<<6) | (0<<2) ; //GPG6,2 output 0
GPEDAT = (GPEDAT &(~((1<<13)|(1<<11)))) | (1<<13) | (1<<11) ; //GPE13,11 output 0
if( (GPFDAT&(1<< 0)) == 0 ) return 16 ;
else if( (GPFDAT&(1<< 2)) == 0 ) return 15 ;
else if( (GPGDAT&(1<< 3)) == 0 ) return 14 ;
else if( (GPGDAT&(1<<11)) == 0 ) return 13 ;
GPGDAT = (GPGDAT &(~((1<<6)|(1<<2)))) | (0<<6) | (1<<2) ; //GPG6,2 output 0
GPEDAT = (GPEDAT & (~((1<<13)|(1<<11)))) | (1<<13) | (1<<11) ; //GPE13,11 output 0
if( (GPFDAT&(1<< 0)) == 0 ) return 11 ;
else if( (GPFDAT&(1<< 2)) == 0 ) return 8 ;
else if( (GPGDAT&(1<< 3)) == 0 ) return 5 ;
else if( (GPGDAT&(1<<11)) == 0 ) return 2 ;
GPGDAT = (GPGDAT & (~((1<<6)|(1<<2)))) | (1<<6) | (1<<2) ; //GPG6,2 output 0
GPEDAT = (GPEDAT & (~((1<<13)|(1<<11)))) | (1<<13) | (0<<11) ; //GPE13,11 output 0
if( (GPFDAT&(1<< 0)) == 0 ) return 10 ;
else if( (GPFDAT&(1<< 2)) == 0 ) return 7 ;
else if( (GPGDAT&(1<< 3)) == 0 ) return 4 ;
else if( (GPGDAT&(1<<11)) == 0 ) return 1 ;
GPGDAT = (GPGDAT & (~((1<<6)|(1<<2)))) | (1<<6) | (1<<2) ; //GPG6,2 output 0
GPEDAT = (GPEDAT & (~((1<<13)|(1<<11)))) | (0<<13) | (1<<11) ; //GPE13,11 output 0
if( (GPFDAT&(1<< 0)) == 0 ) return 12 ;
else if( (GPFDAT&(1<< 2)) == 0 ) return 9 ;
else if( (GPGDAT&(1<< 3)) == 0 ) return 6 ;
else if( (GPGDAT&(1<<11)) == 0 ) return 3 ;
else return 0xff ;
}
void EINT_Handle( void ) {
GPGCON = (GPGCON & (~((3<<22)|(3<<6)))) | ((0<<22)|(0<<6)) ; //GPG11,3 set input
GPFCON = (GPFCON & (~((3<<4)|(3<<0)))) | ((0<<4)|(0<<0)) ; //GPF2, 0 set input
if(INTPND==BIT_EINT8_23) {
if(EINTPEND&(1<<11))
EINTPEND |= 1<< 11;
if(EINTPEND&(1<<19))
EINTPEND |= 1<< 19;
ClearPending(BIT_EINT8_23);
}
else if(INTPND==BIT_EINT0) {
ClearPending(BIT_EINT0);
} else if(INTPND==BIT_EINT2) {
ClearPending(BIT_EINT2);
}
int key = Key_Scan() ;
if( key != 0xff ) {
uart_printf( "K%d is pressed!\n", key ) ;
GPFDAT = ~(key << 4);
}
SET_KEY_INTERRUPT_REG();
return;
}
文件4:mem.s
@ 文件 mem.s
@ 作用:SDRAM 的初始化设置
@ 关于初始化的更多细节,请参考我的前一篇随笔
.global memory_setup @ 导出 memory_setup, 使其对链接器可见
memory_setup:
mov r1, #0x48000000
adrl r2, mem_cfg_val
add r3, r1, #13*4
1:
@ write initial values to registers
ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b
mov pc, lr
.align 4
mem_cfg_val:
.long 0x22111110 @ BWSCON
.long 0x00000700 @ BANKCON0
.long 0x00000700 @ BANKCON1
.long 0x00000700 @ BANKCON2
.long 0x00000700 @ BANKCON3
.long 0x00000700 @ BANKCON4
.long 0x00000700 @ BANKCON5
.long 0x00018005 @ BANKCON6
.long 0x00018005 @ BANKCON7 9bit
.long 0x008e07a3 @ REFRESH
.long 0x000000b2 @ BANKSIZE
.long 0x00000030 @ MRSRB6
.long 0x00000030 @ MRSRB7
文件5:nand_read.c
/* 文件 nand_read.c
* 作用:从 Nand Flash 中读取一块数据到 SDRAM 中的指定位置
*/
#define NFCONF (*(volatile unsigned long *)0x4e000000)
#define NFCMD (*(volatile unsigned long *)0x4e000004)
#define NFADDR (*(volatile unsigned long *)0x4e000008)
#define NFDATA (*(volatile unsigned long *)0x4e00000c)
#define NFSTAT (*(volatile unsigned long *)0x4e000010)
#define NFECC (*(volatile unsigned long *)0x4e000014)
#define NAND_SECTOR_SIZE 512
#define NAND_BLOCK_MASK 0x1ff
void wait_idle() {
int i;
for (i = 0; i < 50000; ++i) ;
}
int nand_read(unsigned char *buf, unsigned long start_addr, int size){
int i, j;
/*
* detect the argument
*/
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
return -1;
}
/* chip Enable */
NFCONF &= ~0x800;
for (i=0; i<10; i++) {
;
}
for (i=start_addr; i < (start_addr + size); i+=NAND_SECTOR_SIZE) {
NFCMD = 0;
/* Write Address */
NFADDR = i & 0xff;
NFADDR = (i >> 9) & 0xff;
NFADDR = (i >> 17) & 0xff;
NFADDR = (i >> 25) & 0xff;
wait_idle();
for(j=0; j < NAND_SECTOR_SIZE; j++) {
*buf++ = (NFDATA & 0xff);
}
}
NFCONF |= 0x800; /* chip disable */
return 0;
}
文件6:sdram.c
/* 文件 sdram.c
* 作用:循环点 FS2410 开发板上的 D9、D10、D11、D12
* 四个发光二极管。
*/
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
int main()
{
int i,j;
while(1) {
for (i = 0; i <4; ++i) {
GPFCON = 0x1<<(8+i*2);
GPFDAT = 0x0;
// for delay
for(j=0;j<50000;++j) ;
}
}
}
文件7:nand.lds
SECTIONS {
first 0x00000000 : { head.o mem.o flash.o nand_read.o }
second 0x30000000 : AT(4096) { sdram.o }
}
文件8:Makefile
sdram:head.s flash.s mem.s sdram.c
arm-linux-gcc -c -o head.o head.s
arm-linux-gcc -c -o mem.o mem.s
arm-linux-gcc -c -o flash.o flash.s
arm-linux-gcc -c -o nand_read.o nand_read.c
arm-linux-gcc -c -o sdram.o sdram.c
arm-linux-ld -Tnand.lds head.o mem.o flash.o nand_read.o sdram.o -o sdram_tmp.o
arm-linux-obj -O binary -S sdram_tmp.o sdram
clean:
rm -f *.o
rm -f sdram
好了,你把这些文件拷下去,执行make命令就能生成可执行的二进制代码sdram,把sdram烧写到板子上就能运行了。祝你好运
⑥ S3C2410中断控制器实验,在参考代码基础上做修改,修改中断控制器各个寄存器的配置,完成初始化函数编写
STM32的EXTI控制器支持19 个外部中断/ 事件请求。每个中断设有状态位,每个中断/ 事件都有独立的触发和屏蔽设置。
STM32的19个外部中断对应着19路中断线,分别是EXTI_Line0-EXTI_Line18:
线0~15:对应外部 IO口的输入中断。
线16:连接到 PVD 输出。
线17:连接到 RTC 闹钟事件。
线18:连接到 USB 唤醒事件。
触发方式:STM32 的外部中断是通过边沿来触发的,不支持电平前尘触发。
外部中断分组:STM32 的每一个GPIO都能配置成一个外部中断触发源,STM32 通过根据引脚的序号不同将众多中断触发源分成不同的组,比如:PA0,PB0,PC0,PD0,PE0,PF0,PG0为第一组,那么依此类推,我们能得出一共有16 组,STM32 规定,每一组中同时只能有一个中断触发源工作,那么,最多工作的也就是16个外部中断。
寄存器组
EXTICR寄存器组,总共有4 个,因为编译器的寄存器组都是从0 开始编号的,所以EXTICR[0]~ EXTICR[3],对应《STM32参考手册》里的 EXTICR1~ EXTICR 4(查了好久才搞明白这个数组的含义!!)。每个 EXTICR只用了其低16 位。
EXTICR[0] ~EXTICR[3]的分配如下:
EXTI寄存器的结构体:
typedef struct
{
vu32 IMR;
vu32 EMR;
vu32 RTSR;
vu32 FTSR;
vu32 SWIER;
vu32 PR;
} EXTI_TypeDef;
IMR:中断屏蔽寄存器
这是一个 32 寄存器。但是只有前 19 位有效。当位 x 设置为1 时,则开启这个线上的中断,否则关闭该线上的中断。
EMR:事件屏蔽寄存器
同IMR ,只是该寄存器是针对事件的屏蔽和开启。
RTSR:上升沿触发选择寄存器
该寄存器同IMR ,也是一个32为的寄存器,只有前 19位有效。位 x 对应线x 上的上升沿触发,如果设置为 1 ,则是允许上升沿触发中断/ 事件。否则,不允许。
FTSR:下降沿触发选择寄存器
同 PTSR,不过这个寄存器是设置下降沿的。下降沿和上升沿可以被同时设置,这样就变成了任意电平触发了。
SWIER:软件中断事件寄存器
通过向该寄存器的位x 写入 1 ,在未设置 IMR 和EMR的时候,将设置PR中相应位挂起。如果设置了IMR 和EMR时将产生一次中断。被设置的SWIER位,将会在PR中的对应位清除后清除。
PR:挂起寄存器
0 ,表示对应线上没有发生触发请求。
1,表示外部中断线上发生了选择的边沿事件。通过向该寄存器的对应位写入 1 可喊禅以清除该位。
在中断服务函数里面经常会要向该寄存器的对应位写1 来清除中断请求。
Ex_NVIC_Config基本是按照这个结构来编写的
中断配置步骤
STM32的每个IO口都可以作为中断输入,这点很好用。要把IO口作为外部中断输入,有以下几个步骤:
1)初始化IO口为输入。
这一步设置你要作为外部中断输入的IO口的状态,可以设置为上拉/下拉输入,也可以设置为浮空输入,但浮空的时候外部一定要带上拉,或者下拉电阻。否则可能导致中断不停的触发。在干扰较大的地方,就算使用了上拉/下拉,也建议使用外部上拉/下拉电阻,这样可以一定程度防止外部干扰带来的影响。
2)开启IO口复用时钟慧渗禅,设置IO口与中断线的映射关系。
STM32的IO口与中断线的对应关系需要配置外部中断配置寄存器EXTICR,这样我们要先开启复用时钟,然后配置IO口与中断线的对应关系。才能把外部中断与中断线连接起来。
3)开启与该IO口相对的线上中断/事件,设置触发条件。
这一步,我们要配置中断产生的条件,STM32可以配置成上升沿触发,下降沿触发,或者任意电平变化触发,但是不能配置成高电平触发和低电平触发。这里根据自己的实际情况来配置。同时要开启中断线上的中断,这里需要注意的是:如果使用外部中断,并设置该中断的EMR位的话,会引起软件仿真不能跳到中断,而硬件上是可以的。而不设置EMR,软件仿真就可以进入中断服务函数,并且硬件上也是可以的。建议不要配置EMR位。
4)配置中断分组(NVIC),并使能中断。
这一步,我们就是配置中断的分组,以及使能,对STM32的中断来说,只有配置了NVIC的设置,并开启才能被执行,否则是不会执行到中断服务函数里面去的。关于NVIC的详细介绍,请参考前面章节。
5)编写中断服务函数。
这是中断设置的最后一步,中断服务函数,是必不可少的,如果在代码里面开启了中断,但是没编写中断服务函数,就可能引起硬件错误,从而导致程序崩溃!所以在开启了某个中断后,一定要记得为该中断编写服务函数。在中断服务函数里面编写你要执行的中断后的操作。
实验4--外部中断实验exit.c函数如下:
[cpp] view plain
#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY2==1) //按键2
{
LED0=!LED0;
LED1=!LED1;
}
EXTI->PR=1<<0; //清除LINE0上的中断标志位
}
//外部中断15~10服务程序
void EXTI15_10_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0) //按键0
{
LED0=!LED0;
}else if(KEY1==0)//按键1
{
LED1=!LED1;
}
EXTI->PR=1<<13; //清除LINE13上的中断标志位
EXTI->PR=1<<15; //清除LINE15上的中断标志位
}
//外部中断初始化程序
//初始化PA0,PA13,PA15为中断输入.
void EXTIX_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟
JTAG_Set(JTAG_SWD_DISABLE);//关闭JTAG和SWD
GPIOA->CRL&=0XFFFFFFF0;//PA0设置成输入
GPIOA->CRL|=0X00000008;
GPIOA->CRH&=0X0F0FFFFF;//PA13,15设置成输入
GPIOA->CRH|=0X80800000;
GPIOA->ODR|=1<<13; //PA13上拉,PA0默认下拉
GPIOA->ODR|=1<<15; //PA15上拉
Ex_NVIC_Config(GPIO_A,0,RTIR); //上升沿触发
Ex_NVIC_Config(GPIO_A,13,FTIR);//下降沿触发
Ex_NVIC_Config(GPIO_A,15,FTIR);//下降沿触发
MY_NVIC_Init(2,2,EXTI0_IRQChannel,2); //抢占2,子优先级2,组2
MY_NVIC_Init(2,1,EXTI15_10_IRQChannel,2);//抢占2,子优先级1,组2
}
其中的两个函数:Ex_NVIC_Config(GPIO_A,0,RTIR);和MY_NVIC_Init(2,2,EXTI0_IRQChannel,2);这两个函数都是在sys.c里定义,分别完成了步骤2、3、4.函数原型如下:
[cpp] view plain
//外部中断配置函数
//只针对GPIOA~G;不包括PVD,RTC和USB唤醒这三个
//参数:GPIOx:0~6,代表GPIOA~G;BITx:需要使能的位;TRIM:触发模式,1,下升沿;2,上降沿;3,任意电平触发
//该函数一次只能配置1个IO口,多个IO口,需多次调用
//该函数会自动开启对应中断,以及屏蔽线
//待测试...
void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM)
{
u8 EXTADDR;
u8 EXTOFFSET;
EXTADDR=BITx/4;//得到中断寄存器组的编号
EXTOFFSET=(BITx%4)*4;
RCC->APB2ENR|=0x01;//使能io复用时钟
AFIO->EXTICR[EXTADDR]&=~(0x000F<<EXTOFFSET);//清除原来设置!!!
AFIO->EXTICR[EXTADDR]|=GPIOx<<EXTOFFSET;//EXTI.BITx映射到GPIOx.BITx
//自动设置
EXTI->IMR|=1<<BITx;// 开启line BITx上的中断
//EXTI->EMR|=1<<BITx;//不屏蔽line BITx上的事件 (如果不屏蔽这句,在硬件上是可以的,但是在软件仿真的时候无法进入中断!)
if(TRIM&0x01)EXTI->FTSR|=1<<BITx;//line BITx上事件下降沿触发
if(TRIM&0x02)EXTI->RTSR|=1<<BITx;//line BITx上事件上升降沿触发
}
这个函数完成了两个步骤:
2、开启IO口复用时钟,设置IO口与中断线的映射关系
3、开启与该IO口相对的线上的中断/时间,设置触发条件
[cpp] view plain
//设置NVIC
//NVIC_PreemptionPriority:抢占优先级
//NVIC_SubPriority :响应优先级
//NVIC_Channel :中断编号
//NVIC_Group :中断分组 0~4
//注意优先级不能超过设定的组的范围!否则会有意想不到的错误
//组划分:
//组0:0位抢占优先级,4位响应优先级
//组1:1位抢占优先级,3位响应优先级
//组2:2位抢占优先级,2位响应优先级
//组3:3位抢占优先级,1位响应优先级
//组4:4位抢占优先级,0位响应优先级
//NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先
//CHECK OK
//100329
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)
{
u32 temp;
u8 IPRADDR=NVIC_Channel/4; //每组只能存4个,得到组地址
u8 IPROFFSET=NVIC_Channel%4;//在组内的偏移
IPROFFSET=IPROFFSET*8+4; //得到偏移的确切位置
MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组
temp=NVIC_PreemptionPriority<<(4-NVIC_Group);
temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);
temp&=0xf;//取低四位
if(NVIC_Channel<32)NVIC->ISER[0]|=1<<NVIC_Channel;//使能中断位(要清除的话,相反操作就OK)
else NVIC->ISER[1]|=1<<(NVIC_Channel-32);
NVIC->IPR[IPRADDR]|=temp<<IPROFFSET;//设置响应优先级和抢断优先级
这个函数完成了:
4、配置中断分组(NVIC),并使能中断
补充
在实验18--触摸屏实验中,中断初始化没有调用这个函数,它是这样配置的:
[cpp] view plain
MY_NVIC_Init(2,0,EXTI1_IRQChannel,2);
RCC->APB2ENR|=0x01; //使能io复用时钟
AFIO->EXTICR[0]|=0X0020; //EXTI1映射到PC1(这句原子的程序里注释错了搞成了EXTI13)
EXTI->IMR|=1<<1; //开启line1上的中断
EXTI->EMR|=1<<1; //不屏蔽line1上的事件
EXTI->FTSR|=1<<1; //line1上事件下降沿触发
RCC->APB2ENR|=0x01; 这一句是开启复用时钟,什么时候需要开启复用时钟?手册有这样一段:
也就是说只要操作EVCR、EXTICRX、MAPR的时候,就必须开启复用功能时钟,即当你要配置stm32的事件输出、外部中断、重映射的时候.就必须开启复用时钟。
AFIO->EXTICR[0]|=0X0020; //EXTI1映射到PC1
这一句设置中断映射,如上文所说EXTICR[0]~ EXTICR[3] 对应 EXTICR1~ EXTICR4,举例:
AFIO->EXTICR[3] &= 0xFFFFFF0F;
AFIO->EXTICR[3] |= 0xFFFFFF0F; //EXTI13映射到PA13,0(即0x00)代表A口,1(即0x01)代表B口,依次类推,6(即0x0110)代表G口.
AFIO->EXTICR[3] &= 0xFFFFFF0F;
AFIO->EXTICR[3] |= 0xFFFFFF2F; //EXIT13映射到PC13,2(0x0010)代表C口
外部中断函数不能进入的原因分析分析,可能为以下几个方面:
1)GPIO或者AFIO的时钟没有开启;
2)GPIO和配置的中断线路不匹配;
3)中断触发方式和实际不相符合;
4)中断处理函数用库函数时,写错,经常可能出现数字和字母之间没有下划线;
5)外部中断是沿触发,有可能检测不到沿,比如中断线是低电平(浮空输入),触发是下降沿触发,可能会出现一直是低电平,高电平的时候是一样的情况,电平持续 为高电平;
6)没有用软件中断来触发外部中断,调用函数EXTI_GenerateSWInterrupt;,因为软件中断先于边沿中断处理。
⑦ 嵌入式是如何做到将某功能植入芯片的
用一片或少数几片大规模集成电路组成的中央处理器。
这些电路执行控制部件和算术逻辑部件的功能。微处理器与传统的中央处理器相比,具有体积小,重量轻和容易模块化等优点。
一) 微处理器的基本组成部分有:寄存器堆、运算器、时序控制电路,以及数据和地址总线。微处理器能完成取指令、执行指令,以及与外界存储器和逻辑部件交换信息等操作,是微型计算机的运算控制部分。它可与存储器和外围电路芯片组成微型计算机。但这些专用操作系统都是商业化产品,其高昂的价格使许多低端产品的小公司望而却步;而且,源代码封闭性也大大限制了开发者的积极性。
二) 而Linux的开放性,使得许多人都认为Linux非常适合多数Intemet设备。Linux操作系统可以支持不同的设备和不同的配置。
Linux对厂商不偏不倚,而且成本极低,因而很快成为用于各种设备的操作系统。嵌入式linux是大势所趋,其巨大的市场潜力与酝酿的无限商机必然会吸引众多的厂商进入这一领域。
三) 嵌入式linux操作系统
Linux是一类Unix计算机操作系统的统称。Linux操作系统的内核的名字也是"Linux".Linux操作系统也是自由软件和开放源代码发展中最着名的例子。严格来讲,Linux这个词本身只表示Linux内核,但在实际上人们已经习惯了用Linux来形容整个基于Linux内核,并且使用GNU工程各种工具和数据库的操作系统。
Linux得名于计算机业余爱好者LinuSTorvalds.Linux的程序源码全部公开,任何人都可以根据自己的需要裁剪内核,以适应自己的系统。
linux移植到ARM920T内核的s3c2410处理器芯片为例,介绍了嵌入式linux内核的裁剪以及移植过程,文中介绍的基本原理与方法技巧也可用于其它芯片。
四) 内核移植过程
1 ,建立交叉编译环境
在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,我们就称这种编译器支持交叉编译。
这个编译过程就叫交叉编译。简单地说,就是在一个平台上生成另一个平台上的可执行代码。这里需要注意的是所谓平台,实际上包含两个概念:体系结构(Architecture)、操作系统(Operating System)。
2,同一个体系结构可以运行不同的操作系统;同样,同一个操作系统也可以在不同的体系结构上运行。举例来说,我们常说的x86 Linux平台实际上是Intel x86体系结构和Linux for x86操作系统的统称;而x86 WinNT平台实际上是Intel x86体系结构和Windows NT for x86操作系统的简称。
3,交叉编译交叉编译呢,简单地说,就是在一个平台上生成另一个平台上的可执行代码。这里需要注意的是所谓 平台,实际上包含两个概念:体系结构(Architecture)、操作系统(Operating System)。同一个体系结构可以运行不同的操作系统;同样,同一个操作系统也可以在不同的体系结构上运行。
4,交叉编译器完整的安装涉及到多个软件安装,最重要的有binutils、gcc、glibc三个。其中,binutils主要用于生成一些辅助工具;gcc则用来生成交叉编译器,主要生成arm-linux-gcc交叉编译工具;glibc主要是提供用户程序所使用的一些基本的函数库。
5,自行搭建交叉编译环境通常比较复杂,而且很容易出错。本文使用的是开发板自带的交叉编译器,即CROSS一3.3.4.交叉编译器,该编译只需将光盘中的arm-linux一3.3.4.bar.bz2用tar ixvf arm-linux一3.3.4.bar.bz2命令解压到/usr/local/arm下即可。
五) 修改Makefile
Makefile文件Makefile一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
修改内核目录树根下的Makefile时,可先指明交叉编译器。设计时,可向Makefile中添加如下内容:
ARCH ?=arm
CROSS_COMPILE?=arm-linux-然后设置PATH环境变量,使其可以找到其交叉编译工具链,然后运行vi~/.bashrc,再添加如下内容:
export PATH=/usr/local/arln-linux一3.4.4/bin:$PATH
六) 设置Flash分区
此处一共要修改3个文件,分别如下:
(1)在arch/arm/machS3C2410/devs.c文件中添加如下内容:
#include
#include
#include
然后再建立Nand flash分区表;同时建立Nand F1ash芯片支持,最后加入Nand FLASH芯片并支持到Nand Flash驱动。
另外,还要修改arch/arm/machs3c2410/devs.C文件中的s3c_device_nand结构体变量,同时添加对dev成员的赋值。
(2)指定启动时初始化
内核启动时,可以依据对分区的设置进行初始配置,然后修改arch/am4mach-s3c2410/machsmdk2410.e文件下的smdk2410_devices[],指明初始化时包括在前面所设置的flash分区信息,并添加如下语句:
&s3c_device_nand,
(3)禁止Flash ECC校验
内核一般都是通过UBOOT写到Nand Flash的。UBOOT则通过软件ECC算法来产生ECC校验码,这与内核校验的ECC码不一样,内核中的ECC码是由S3C2410中Nand Flash控制器产生的。所以,这里选择禁止内核ECC校验。
修改drivers/mtd/nand/s3c2410.C 下的s3c2410_nand_init_chip ()函数,可在该函数体最后加上如下一条语句:
chip->eccmode=NAND_ECC_NONE;
⑧ S3C2410A复位后执行的第一条指令存放首地址是什么
下面是我个人的一点认知,希望对你有帮助。
1、从nor flash启动则存在地址0x00000000,也就是说要用FLASH做程序启动,FLASH必须放BANK0,且地址从0x0开始,你可以直接在nor flash 中运行程序,也可把程序用自己写在启动代码拷入SDRAM运行。
2、若从NAND FLASH启动,则存放在NAND FLASH的开始区域,上电后系统会硬拷贝到内部的4KBRAM运行。
下面是我写的一个直接从nor启动的程序代码:
只有init.s与led.c两个文件,我调试过,运行正常
ADS环境参数设置中把:RO-BASE设为0x00000000,RW-BASE设为0x30000000(根据具体硬件来定)
image entry point 设为:0x00000000,
设置好的合令为:-info totals -entry 0x00000000 -ro-base 0x00000000 -map -symbols -rw-base 0x31000000 -first init.o(Init) -xref
init.s文件内容:
AREA Init,CODE,READONLY ;该伪指令定义了一个代码段,段名为Init,属性只读
ENTRY ;程序的入口点标识
ResetEntry
ldr sp,=0x31000000 ;定义堆栈指针(根据具体硬件来定)
IMPORT Main ;声明主函数Main
BL Main ;调用主函数
END ;程序结束符
led.C代码:
/*led.c*/
/*用GPB5~GPB8控制四个发光二极管闪烁*/
#define GPBCON (*(volatile unsigned *)0x56000010) //Port B control
#define GPBDAT (*(volatile unsigned *)0x56000014) //Port B data
#define GPBUP (*(volatile unsigned *)0x56000018) //Pull-up control B
void Main(void)
{
int i;
GPBCON &= ~((0x3<<10) | (0x3<<12) | (0x3<<14) | (0x3<<16
GPBCON |= (0x1<<10) | (0x1<<12) | (0x1<<14) | (0x1<<16); //GPBCON=0x00015400;
while (1)
{
GPBDAT=0x061F;
for(i=0;i<10000;i++);
GPBDAT=0x07FF;
for(i=0;i<10000;i++);
}
}