北美吐槽君 我要进来了:嵌入式---程序示例

来源:百度文库 编辑:中财网 时间:2024/04/29 14:34:26
>>Drew的主页---->程序示例
主页
VxWorks
ARM
PowerPC
硬件驱动
网络协议
程序示例
技术论坛下载
个人兴趣
程序示例
这里是一些程序示例和源码解释.一些相关程序代码会陆续补上.相关问题可参见硬件驱动
系统初始化(PowerPC汇编)VxWorks BSP文件配置及生成下载
硬件中断VxWorks网络驱动
FlashROM驱动(ARM汇编)ARM Boot Load程序(ARM汇编)
串口(UART)程序示例PCI设备(网卡)初始化代码分析
文件系统
LCD和触摸屏
MODEM 拨号
实时时钟 RTC
系统初始化
系统初始化对不同的CPU,基本步骤是类似的.
系统初始化的主要步骤如下
启动 关闭中断 放boot type到堆栈 清空缓存
VxWorks 系统的 PowerPC BSP,系统开机后执行的第一个函数 romInit(),在ROM的起点,这里是使用的PowerPC汇编语言
/* 定义内部函数 internals */
.globl romInit /* start of system code */
.globl _romInit /* start of system code */
/* 定义外部函数 externals */
.extern romStart /* system initialization routine */
.text
.align 2
/*******************************************************************************
*
* romInit ( int startType /@ only used by 2nd entry point @/ )
*/
romInit:
_romInit:
bl cold    /* 冷启动 */
bl warm    /* 热启动 */
cold:
li p5, BOOT_COLD
bl start /* skip over next instruction */
warm:
or p5, p0, p0 /* startType to p5 */
start:        /* 此处是系统启动开始 */
/* 屏蔽MSR中CE,EE位,关闭所有的外部中断
/*
* Disable external interrupts
*/
mfmsr p0 /* p0 = msr */
INT_MASK (p0, p1) /* mask EE and CE bit */
ori p1,p1,_PPC_MSR_ME /* enable machine checks */
mtmsr p1 /* msr = p1 */
isync
/* 下面两步是按照硬件定义初始化一些SPR,DCR寄存器,置0或置1
/* SPR是特殊功能寄存器,DCR为设备控制寄存器,还有MSR机器状态寄存器,这些是PowerPC内核中很重要的寄存器
/* 初始化SPR,DCR寄存器置0
* Initalize registers that need to be set to zero.
*/
addi r4,r0,0x0000
mtspr SGR,r4 /* 解锁所有存储区域 SPR 中 SGR 位置0 */
mtspr ESR, r4 /* SPR中的错误状态位 ESR 清0 */
mtspr TCR, r4 /* 关闭所有的 timers */
mtspr PIT, r4 /* 清0 PIT timer */
mtdcr UICER, r4 /* 关闭中断控制器(UIC)中的所有中断 */
mtspr XER, r4 /* 清0 integer exception 寄存器 */
/* 初始化另一些SPR,DCR寄存器置1
* Initalize registers that need to be cleared with 0xFFFFFFFF.
*/
addis r4,r0,0xffff
ori r4,r4,0xffff
mtspr TSR, r4 /* timer  */
mtspr DBSR, r4 /* 调试状态位置1 */
mtdcr UICSR, r4 /* 清除中断控制器(UIC)中的所有 pending 中断 */
mtdcr dmasr, r4 /* DMA状态寄存器置1 */
/* PowerPC405用两个缓存,一个是16K指令缓存(ICU),一个是6K数据缓存(DCU),下面是清空着两个缓存,并根据硬件设置缓存 */
/* 清空指令缓存 */
/*BESR type regs ZZZZZZZZZZZZ
* Invalidate the entire instruction cache. This can be done
* with a single iccci instruction in the processor core.
*/
iccci r0, r0
/*清空数据缓存
* Invalidate the entire data cache.
* The 405 processor core in the 405GP has 128 congruence classes.
* Each cache line in the 405 processor is 32 bytes.
*/
/*
* Turn the instruction cache on for faster boot-up.
* Also, the icache is needed to help initialize Bank 0
* of the EBC to speed up accesses to flash.
* address space 0x00000000-0x07ffffff is cached
* address space 0xf8000000-0xffffffff is cached
*/
lis p0, HIADJ(_PPC403_ICCR_DEFAULT_VAL)
addi p0, p0, LO(_PPC403_ICCR_DEFAULT_VAL)
mtspr _PPC403_ICCR, p0
isync
/* 初始化外部总线控制器(EBC),跳转指令BL,执行extBusCntlrInit
* /
bl extBusCntlrInit
/*
* Now that the EBC Bank 0 has been set up, turn the I-cache off if
* i-cache was not specified in config.h. It is also invalidated
* again.
*/
#ifndef USER_I_CACHE_ENABLE
li p0, 0 /* clear p0 */
mtspr _PPC403_ICCR, p0 /* turn off i-cache */
isync
iccci r0, r0 /* invalidate the I-cache again */
#endif
/* 初始化和SDRAM相关的IIC(inter-integrated circut)寄存器IIC0,
* Initialize IIC0 for use in automatic SDRAM configuration
*/
#ifdef LOCAL_MEM_AUTOSIZE
bl iic0Init
#endif
/* 初始化SDRAM,BL跳转执行sdramInit
* Configure the SDRAM controller only if this is a cold boot.
* If the SDRAM controller is reinitialized on a warm boot, the
* boot line will get wiped out because of the ECC SDRAM memory
* initialization.
*/
li p0, BOOT_COLD
and. p0, p0, p5 /* p5 is saved at the entry of romInit */
beq skip
bl sdramInit
skip:
/*
* Clear the CPU reservation bit
*/
li r0, 0
lwarx p0, r0, r0
stwcx. p0, r0, r0
#ifdef PPC405GP_REVA
/* 设置中断向量表到0x0000 */
li p0, 0x2100/4
mtctr p0
lis p0, WALNUT_EVPR_VAL
li p1, 0x0000
zeroOut:
stw p1,0x0(p0)
addi p0, p0, 4
bdnz zeroOut
#endif
/* 初始化堆栈
/* Initialize the stack pointer (r1)  */
lis sp, HIADJ(STACK_ADRS)
addi sp, sp, LO(STACK_ADRS)
#if FALSE /* SDA not supported */
/* initialize r2 and r13 according to EABI standard */
lis r2, HIADJ(_SDA2_BASE_)
addi r2, r2, LO(_SDA2_BASE_)
lis r13, HIADJ(_SDA_BASE_)
addi r13, r13, LO(_SDA_BASE_)
#endif
/* 得到C程序romStart()在ROM中的地址,保证romInit执行结束后,系统跳转执行romStart()
/* calculate C entry point: routine - entry point + ROM base */
lis p1, HIADJ(romStart) /* p1 = romstart */
addi p1, p1, LO(romStart)
lis p2, HIADJ(romInit) /* p2 = romInit */
addi p2, p2, LO(romInit)
/* ROM_TEXT_ADRS为ROM的入口地址,在文件makefile定义,为0xfff80100
lis p3, HIADJ(ROM_TEXT_ADRS) /* p3 = ROM_TEXT_ADRS */
addi p3, p3, LO(ROM_TEXT_ADRS)
subf p1, p2, p1 /* p1 = p1 - p2 */
add p1, p1, p3 /* p1 = p1 + p3 */
/* p1中是romStart()的地址,这里把这个地址放到连接寄存器LR中.         mtlr p1 /* link register = C entry point */
or p0, p5, p5 /* p0 = startType */
addi sp, sp, -FRAMEBASESZ /* get frame stack */
/* 跳转到LR中romStart()的地址,执行romStart()
blr /* branch to link register */
返回页首
硬件中断
中断的产生和VxWorks系统的中断操作:
一般中断的产生是由硬件定义的,如串口中断的定义:
1.接收中断:当接收中断使能,接收数据存储器 RxData 存在有效数据,则产生中断.
2.发送中断:当发送中断使能,发送数据存储器 TxData 为空,则产生中断.

硬件发送中断产生逻辑示意

硬件接收中断产生逻辑示意
所以要产生一个串口中断,主要有两步:
1.使能这两个串口中断,RX Enable,TX Enable,函数intEnable().
2.用intConnect()登记中断号,和相应的中断例程ISR.
程序示例
在VxWorks系统上登记,使能串口中断
intConnect((VOIDFUNCPTR *)5,ComISR,0);         //登记中断服务程序ComISR()到外部中断号5,
intEnable((VOIDFUNCPTR *)5);                   //使能外部中断5
//enable 使能 UART1 这里直接用32位地址表示了
*(unsigned long *)0x20000014 &= ~0x01000100;  //INT_FORCE     use pending
*(unsigned long *)0x20000010 = 0x01000100;     //INT_PENDING    clear
*(unsigned long *)0x20000018 = 0x01000100;    //INT_MASK only enable UART1 RX
//control register 使能 RX TX
*(unsigned long *)0x20000068 = 0x00070007;    //RX and TX ENABLE
//divider register,baut rate 19200    设置波特率
*(unsigned long *)0x2000006c = 59;
//完成, 这样当串口有中断发生时,硬件系统会自动跳转到中断号5的地址0x18,调用程序ComISP().
注意:中断程序不能单步执行,或跟踪调试,中断服务程序中与函数库或系统有关的函数不可用如print()等,因为中断调用时,所有其它的任务都被挂起停止运行.
返回页首
VxWorks系统的网络驱动(END)
VxWorks网络配置参见VxWorks网络驱动配置及分析
VxWorks系统网络驱动在BSP中完成,写驱动时应参考BSP develop kit,在VxWorks中叫做END( Enhanced Network Driver),编写程序使用由VxWorks定义的MUX接口
MUX是数据链路层和网络协议层之间的接口
主要调用过程和步骤如下:
VxWorks系统执行的第一个任务target\config\all\usrConfig.c文件中 usrRoot()=======>>
target\src\config\usrNetwork.c文件(该文件初始化TCP/IP)中 usrNetInit(BOOT_LINE_ADRS)(该函数作用是添加MUX END)========>>
pcooki = pCookie = muxDevLoad(pDevTbl->unit,.....)其中pDevTbl在BSP网络配置文件configNet.h中定义.END_TBL_ENTRY endDevTbl[]={...},该表定义了网络设备的具体参数,在这里调用了网络驱动
END_TBL_ENTRY endDevTbl [] =
{
{0, IBM_EMAC_LOAD_FUNC, IBM_EMAC_LOAD_STR_0, TRUE, NULL, FALSE},
{0, END_TBL_END, NULL, 0, NULL, FALSE},
};
其中IBM_EMAC_LOAD_FUNC就是 ibmEmacEndLoad()
========>>muxDevStart(pcooki)==========>>ibmEmacEndLoad()
ibmEmacEndLoad()初始化系统为网络驱动运行做准备
MUX调用ibmEmacStart()
ibmEmacStart() 登记中断服务程序ibmEmacInit(),启动设备运行在中断模式下.
LOCAL STATUS ibmEmacStart ( EMAC_DRV_CTRL * pDrvCtrl )
{
int rc;
SYS_INT_CONNECT (pDrvCtrl, ibmEmacInt, pDrvCtrl, &rc);
SYS_OUT_LONG(pDrvCtrl, EMAC_ISR, 0xFFFFFFFF);
SYS_INT_ENABLE ();
/* Allow MAL EOB and Descriptor error interrupts */
malChannelIntMaskSet(MAL_TX_TYPE, pDrvCtrl->txChn0MalChannel,
MAL_EOB_INT_EN | MAL_DE_INT_EN | MAL_SERR_INT_EN);
malChannelIntMaskSet(MAL_RX_TYPE, pDrvCtrl->rxChn0MalChannel,
MAL_EOB_INT_EN | MAL_DE_INT_EN | MAL_SERR_INT_EN);
return (OK);
}
中断服务程序ibmEmacInit() 处理EMAC控制器的中断,主要是 TX,RX状态错误
LOCAL void ibmEmacInt ( EMAC_DRV_CTRL * pDrvCtrl )
{
UINT isrReg;
/* Read the EMAC interrupt status register */
SYS_IN_LONG(pDrvCtrl, EMAC_ISR, isrReg);
pDrvCtrl->errorEmac = isrReg;
/*
* Check to see if there was a TX error. If there was, the Dead bit
* will be set. Clear the status bits for the TX error, and clear the dead
* bit. Keep count of these errors in the main device structure.
*/
if (isrReg & EMAC_ISR_TX_INTS)
{
pDrvCtrl->intErrorTX++;
SYS_OUT_LONG(pDrvCtrl, EMAC_ISR, EMAC_ISR_TX_INTS);
}
/*
* Check to see if there was a RX error. Clear the status bits for the RX
* error. Keep count of these errors in the main device structure.
*/
if (isrReg & EMAC_ISR_RX_INTS)
{
pDrvCtrl->intErrorRX++;
SYS_OUT_LONG(pDrvCtrl, EMAC_ISR, EMAC_ISR_RX_INTS);
}
return;
}
(未完)
Cillus网卡CS8900A Linux驱动
下面是我为一个网友解释的CS8900A网卡驱动文件中的部分函数,操作系统为ucLinux,CPU是国内常用的Motorola龙珠系列MC68EZ328(16M),相比PowerPC和ARM来说,它的结构简单,不带MMU,较易理解
CS8900A是一个16位网卡,支持ISA总线,10-BastT.
static inline void outw(unsigned short value,unsigned int addr)
{
unsigned short newvalue;
unsigned char *_src=(unsigned char*)(&value),*_dest=(unsigned char*)(&newvalue);
//这里为什么要交换一下?
////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//这里不是交换,而是赋值,将16位分成两个8位的数组,分别赋值,16位寄存器是只有后8位可读写,
//前8位是寄存器ID,所以这里吧后8位放在前面"_dest[0]=_src[1];"
//然后通过I/O口,赋给相应的存储器,即"*(volatile unsigned short*)addr=newvalue;"
///+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_dest[0]=_src[1];
_dest[1]=_src[0];
*(volatile unsigned short*)addr=newvalue;
}
static int cs89x0_probe1(struct device *dev, int ioaddr)
{
struct net_local *lp;
static unsigned version_printed = 0;
int i;
unsigned rev_type = 0;
irq2dev_map[0] = dev;
/* set up the chip select */
//下面这一段该怎么理解?看不懂阿
////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//这里是对CPU进行操作,对cs89进行初始化,我没有见到你的硬件原理图,我只能按CPU
//的结构定义来解释一下,你要想搞清楚,只有看硬件原理图,还要参考CPU的硬件手册
////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//连通PORT F I/O功能管脚1,SEL1(-IRQ5/PF1),
*(volatile unsigned char *)0xfffff42b |= 0x02; /* output /sleep */
//设置PORT F 管脚0为输出,输出高电平
*(volatile unsigned short *)0xfffff428 |= 0x0101; /* not sleeping */
//连通PORT F 中断功能管脚1,SEL1(-IRQ5/PF1)
*(volatile unsigned char *)0xfffff42b &= ~0x02; /* input irq5 */
//PORT F 管脚1,SEL1(-IRQ5/PF1) 为输入方式,置低电平有效
*(volatile unsigned short *)0xfffff428 &= ~0x0202; /* irq5 fcn on */
//在寄存器CSGBB中定义片选及cs89的基地址为:0x10000000.++++非常重要+++++++
*(volatile unsigned short *)0xfffff102 = 0x8000; /* 0x04000000 */
//在寄存器CSB中,片选使能,cs89地址空间大小(1M),数据线宽16位,6个等待周期,FLASH...,
//非保护存储空间(128K)......
*(volatile unsigned short *)0xfffff112 = 0x01e7; /* 128k, 2ws, FLASH, en */
//对PORT G操作........
*(volatile unsigned int *)0xfffff430 = 0x023c3d0a;
////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//这是I/O模式的主要的两个PORT口,一个是地址口,一个是数据口,这里对0x22操作,
//中断IRQ0
///+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*(volatile unsigned short*)0x1000030a=0x2200;
*(volatile unsigned short*)0x1000030c=0;
//*(volatile unsigned short *)0xfffff302 |= 0x0080;
......................
.....................
void
reset_chip(struct device *dev)
{
int reset_start_time;
writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
/* wait 30 ms */
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 3;
schedule();
/* Wait until the chip is reset */
//这里jiffies说是timestamp,时间标记,这个变量和current
//都在哪里定义?
//jiffies主要起什么作用?
////+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//这里是个延时等待,reset以后必须有等待,以便reset彻底完成,在reset过程中所有寄存器是关闭的
//不但对cs89,对其他芯片也是一样的
///++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  reset_start_time = jiffies;
while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time< 2);
}
static int
net_open(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int result = 0;
int i;
write_irq(dev, lp->chip_type, 0);
irq2dev_map[/* FIXME */ 0] = dev;
writereg(dev, PP_BusCTL, 0); /* ints off! */
//这里和上面那个一样,怎么看懂?
////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//这里也是CPU对CS89操作,我也根据CPU硬件手册给你大概说一下
///+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//中断信号,正电平触发(POL5)
*(volatile unsigned short *)0xfffff302 |= 0x0080; /* +ve pol irq */
//调整PORT G的输入输出状态,及设置管脚相应的电平信号
*(volatile unsigned int *)0xfffff430 = 0x023c3d0a;    /* low -> high */
*(volatile unsigned int *)0xfffff430 = 0x023e3d0a;
*(volatile unsigned int *)0xfffff430 = 0x023c3d0a;    /* high -> low */
////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//这是I/O模式的主要的两个PORT口,一个是地址口,一个是数据口,这里对0x22操作,
//中断IRQ0
///+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*(volatile unsigned short *)0x1000030a = 0x2200;    /* index window, REG index : 0022h */
*(volatile unsigned short *)0x1000030c = 0x0000;    /* data window, REG data : 0000h,irq0 */
.........................
.........................
static void
net_rx(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
struct sk_buff *skb;
int status, length;
//下面两行怎么右边是一样的?
//是作者写错了吗?
////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++|
//不是,这是一个包中前后不同的两个数据,前一个数据是该数据包的状态,后一个是数据包的长度
////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
status = inw(ioaddr + RX_FRAME_PORT);
length = inw(ioaddr + RX_FRAME_PORT);
if ((status & RX_OK) == 0) {
lp->stats.rx_errors++;
if (status & RX_RUNT) lp->stats.rx_length_errors++;
if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
/* per str 172 */
lp->stats.rx_crc_errors++;
if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
return;
}
......................
static int
set_mac_address(struct device *dev, void *addr)
{
int i;
if (dev->start)
return -EBUSY;
if(get_arena_addr())
{
memcpy(dev->dev_addr,0x1100000,6);
}
else
{
//这里的00hhcnl和前面
//出现的00hhcn是什么意思?
////+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//注意:在这里他更改了网卡的MAC值,随便给了一个值"0x00,0x00,‘h‘,h‘,‘c‘,‘n‘",
//不过这个48位值必须是世界唯一的,或者用网卡自带的也可以.我不能确定是否eeprom中有信息,
//你可以用示波器量一下EEDI管脚,若为高电平,则EEPROM用到了,否则,他们根本没有用EEPROM.
///++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
unsigned char mac[]={0,0,‘h‘,‘h‘,‘c‘,‘n‘};
memcpy(dev->dev_addr,mac,6);
}
printk("%s: Setting MAC address to ", dev->name);
for (i = 0; i < 6; i++)
printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
printk(".\n");
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
return 0;
}
返回页首
LCD 与触摸屏
返回页首
Modem拨号
Modem拨号程序步骤如下
1. off hook,发出摘机命令(ATH1\r),
等待modem返回状态,如果是"OK",则表示成功.进行第二步.
2. on hook 发出挂机命令(ATH\r),
等待modem返回状态,如果是"OK",则表示成功.进行第三步.
3. off hook,dialing,摘机拨号(ATDT....\r),
等待modem返回状态,如果是"CONNECT",则表示成功.进行第四步.
4. send data,发送数据.
最好按上述步骤一步一步调,并要等待正确的返回值后再进行下一步.
具体程序例子如下,拨号:
// 清空接收FIFO
REG(FCR, nsChan) |=FCR_RXCLR;
// 等待,直到清空为止
while((REG(FCR, nsChan) & FCR_RXCLR) != 0x00);
// 拨号
while(atdt1[i]!=0)
{
// 检查发送FIFO是否准备好,为空
while((REG(LSR, nsChan) & LSR_THRE) == 0x00);
// 在发送FIFO中放入数据
REG(THR, nsChan) = atdt1[i];
i++;
}
// 等待是否有数据到达,即modem返回数据.
while((REG(LSR, nsChan) & LSR_DR) == 0x00);
// 接收数据并判断返回值
response[i-19]=REG(RBR,nsChan);
if(response[0]!=0x43)
..................
else
..................
// 如果返回值是"connect",
while(*data!=0)
{
while((REG(LSR, nsChan) & LSR_THRE) == 0x00);
REG(THR, nsChan) = *data++;
}
其他步骤基本一样.
这是在ARM7上做的modem驱动,硬件设计方法是modem集成在主板上,没有用串口,modem是用片选地址直接对硬件操作的.
返回页首
RTC
返回页首