基于S3C2440的ARM9基础程序设计及分析

基于S3C2440的ARM9基础程序设计及分析
-------JustDo
最近学习了ARM9基础程序的设计,记录下所学心得,希望对初学者提供一些帮助。学习ARM程序要注意方法,先去芯片手册的相应模块部分的结构原理框图,搞清楚所使用的模块的基本原理,然后根据框图搞清楚需要设置哪些对应的寄存器,再根据时序图搞明白相应寄存器应该怎样设置,再接着理清楚你所写程序的大概流程,最后就可以动手写程序了。
如果把流程理清楚了,你的程序基本上已经成功一半了。
我使用的是S3C2440 ARM芯片,如有疑问或者遗漏之处,请联系:qq,843308498。

源码下载地址http://download.csdn.net/source/3149713
一 LED流水灯程序
流程:
1,设置端口控制寄存器将LED对应端口设为输出模式 (GPxCON)。
2,向端口对应的数据寄存器发送数据(GPxDAT)。
实现:
//这段宏定义了使对应LED亮应该送的数据
#define LED1_ON ~(1<<5)
#define LED2_ON ~(1<<6)
#define LED3_ON ~ (1<<7)
#define LED4_ON ~(1<<8)

#define LED1_OFF (1<<5)
#define LED2_OFF (1<<6)
#define LED3_OFF (1<<7)
#define LED4_OFF (1<<8)

//LED初始化函数,设置GPBCON为输出端口,并往端口送数据,使所有LED处于灭的状态
void LEDInit()
{
rGPBCON |= (1<<10) | (1<<12) | (1<<14) | (1<<16);
rGPBDAT |= (LED1_OFF | LED2_OFF |LED3_OFF | LED4_OFF);
}

//LED轮流闪亮函数,先点亮对应LED,然后延时,再关闭
void LEDRun()
{
rGPBDAT &= LED1_ON;
delay(1000);
rGPBDAT |= LED1_OFF;
rGPBDAT &= LED2_ON;
delay(1000);
rGPBDAT |= LED2_OFF;
rGPBDAT &= LED3_ON;
delay(1000);
rGPBDAT |= LED3_OFF;
rGPBDAT &= LED4_ON;
delay(1000);
rGPBDAT |= LED4_OFF;
}
二 按键中断程序
流程:
中断处理流程
1 中断控制器汇集各类外设发出的中断信号,然后告诉CPU
2 CPU保存当前程序的运行环境,然后调用中断服务程序来处理中断
3 在ISR中通过读取中断控制寄存器,外设相关的寄存器来识别是哪个中断,并进行相应的处理
4 清楚中断:通过读写相关的中断控制寄存器和外设相关寄存器来实现
5 最好恢复被中断程序的环境,继续执行
按键中断程序流程:
初始化
1 设置按键对应的端口为中断(GPxCON)
2 设置中断程序入口
3 清外部中断挂起寄存器EINITPEND和挂起寄存器INTPND,SRCPND,防止原有干扰
4 设置电平触发方式(EXTINITn)
5 关闭外部中断ENTMASK和INTMASK对中断的屏蔽
中断程序
6清挂起寄存器INTPND,SRCPND,防止反复响应该中断
7 通过判断rEINITPEND相应的位来确定是哪一个按键产生的中断,然后执行相应的程序,并清除rEINITPEND相应的位,防止反复的响应

程序:
static void __irq KeyHander();//声明中断处理函数
//按键初始化函数
void KeyInit()
{
MMU_Init();

rGPGCON |= (1<<1 | 1<<7 | 1<<11 | 1<<13 );//设置GPGCON相应端口为中断

pISR_EINT8_23 = (U32)KeyHander;//设置中断程序入口

rEINTPEND |= (1<<8 | 1<<11 | 1<<13 | 1<<14); //清外部中断挂起寄存器,防止干扰
ClearPending(BIT_EINT8_23);

rEINTMASK &= ~(1<<8 | 1<<11 | 1<<13 | 1<<14); //打开外部中断屏蔽器
//rEXTINIT1为设置中断触发方式寄存器,因为其默认是低电平触发,所以不需要设置
EnableIrq(BIT_EINT8_23);//使能中断,其实是打开MASK
}

//按键响应函数
void KeyHander()
{
ClearPending(BIT_EINT8_23);//清中断挂起寄存器包括SRCPND INTPND
if(rEINTPEND & 1<<8)//通过外部中断挂起寄存器判断是哪个按键请求中断
{
rEINTPEND |= 1<<8;//清外部中断挂起寄存器,防止反复中断
LightLed(1);
}
if(rEINTPEND & 1<<11)
{
rEINTPEND |= 1<<11;
LightLed(2);
}
if(rEINTPEND & 1<<13)
{
rEINTPEND |= 1<<13;
LightLed(3);
}
if(rEINTPEND & 1<<14)
{
rEINTPEND |= 1<<14;
LightLed(4);
}
}
三 定时器中断程序
CPU时钟设置流程
1 设置MPLLCON寄存器设置FCLK
2 设置CLKDIVN寄存器设置FCLK:HCLK:PCLK的值
时钟设置流程
初始化
1 设置分频值TCFG0 TCFG1
2 设置定时器计数值 TCNTB0
3 设置中断程序入口,清挂起寄存器和关闭MASK屏蔽
4 更新TCTNB0和TCMTB0的值 TCCON
5 将TCCON清0,否则就会cpu就会一直在更新
启动定时器
6 启动定时器,TCCON
中断函数
清挂起寄存器,执行响应代码

void SetSysClk()//FCLK=405MHZ FCLK:HCLK:PCLK=1:4:8
{
rMPLLCON = 0;
rMPLLCON |= (0x3f<<12 | 2<<4 | 2<<0);//设置FCLK的值
rCLKDIVN |= (1<<2 | 1<<0);//设置FCLK:HCLK:PCLK的比值
//LOCKTIME不需要设置,如没设置,系统将自动选择LOCKTIME
}

void Timer0Init()//PCLK=50MHZ
{
rTCFG0 = 49;
rTCFG1 |= 2; //CLK=62500HZ
//TCFG0 TCFG1设置定时器预分频值和分配值 然后得出定时器0频率:FCLK/(49+1)/16
rTCNTB0 = 62500;
//TCNTB0设置定时器的计数寄存器

ClearPending(BIT_TIMER0);//清除计数器0中断挂起寄存器
pISR_TIMER0 = (U32)Timer0Handle;
EnableIrq(BIT_TIMER0);//打开MASK对定时器0的屏蔽

rTCON |=(1<<1); //更新TCNTB0 和 TCMTB0的值
rTCON =0; //将TCON的值清0,注这一步很重要,如没有,定时器将一直更新TCNTB0和TCMTB0的值导致无法启动
rTCON |= (1<<3 | 1<<0);//启动定时器0,并设置为自动装载

}

static void __irq Timer0Handle()
{
ClearPending(BIT_TIMER0);//清除TIMER0中断挂起寄存器,防止其反复相应
LedRun();
}
四,串口发送程序
流程:
初始化
1 设置相应的GPIO端口为串口输出端口
2 设置串口发生数据格式ULCONn
3 设置接收和发生方式 ULCONn
4 设置波特率rUBRDIVn
发送数据
5 等待发送缓冲区为空 rUTRSTATn
6 向发生数据寄存器写数据 UTXHn
程序:
void UartInit(int num, int buad)
{
int i;
switch(num)
{
case 0:
rGPHCON |= (1<<5 | 1<<7);//设置GPH端口为串口
rULCON0 = 0x03;//设置数据格式为8位数据
rUCON0 = (1<<0 | 1<<2); //设置接收和发送为查询模式
rUBRDIV0 = ((int)PCLK/(16*buad) - 1);//设置波特率
break;
case 1:
rGPHCON |= (1<<9 | 1<<11);
rUBRDIV1 = ((int)PCLK/(16*buad) - 1);
rULCON1 = 0x03;
rUCON1 = (1<<0 | 1<<2);
break;
case 2:
rGPHCON |= (1<<13| 1<<15);
rUBRDIV2 = ((int)PCLK/(16*buad) - 1);
rULCON2 = 0x03;
rUCON2 = (1<<0 | 1<<2);
break;
default:
break;
}

for( i=0; i != 100; ++i);//延时,使设置生效
}

void UartPrintf(char *fmt, ...)
{
va_list ap;
char string[256];

//将不定参数格式化成字符串string
va_start(ap, fmt);
vsprintf(string, fmt, ap);
UartSendString(string);
va_end(ap);
}

void UartSendString(char *str)
{
while(*str)
Uart_SendByte(*str++);
}

void UartSendByte(char byte)
{
switch(whichUart)
{
case 0:
while( ! (rUTRSTAT0 & 0x02));//等待发生区为空
WrUTXH0(byte);
break;

case 1:
while( ! (rUTRSTAT1 & 0x02));
WrUTXH1(byte);
break;

case 2:
while( ! (rUTRSTAT2 & 0x02));
WrUTXH2(byte);
break;

default:
break;
}
}
五,ADC转换程序
流程
初始化
1 设置转换通道和转换频率ADCCONn
2 清相应的中断挂起寄存器,设置中断入口程序,取消MASK屏蔽
启动
3 设置ADCCONn相应的位启动转换
中断服务程序
4通过SUBSRCPND判断是不是普通的ADC转换
5 清SUBSRCPND相应的位
6 读取转换值 ADCDATn
程序
void ADCInit()
{
int channel = 0, preScaler = 50000000/ADCFRE - 1;
rADCCON = (channel<<3) | (preScaler<<6) | (1<<14);//设置ADC通道和预分频值
Delay(100);

pISR_ADC = (U32)ADCHandle;//设置中断向量表
ClearPending(BIT_ADC);//清挂起寄存器
rSUBSRCPND &= (1<<10);//清子中断挂起寄存器
EnableIrq(BIT_ADC);//使能ADC中断,实际是关闭INTMSK对ADC的屏蔽
rINTSUBMSK &= (0<<10);//关闭子中断对ADC_S的屏蔽

rADCCON |= 1;//开始转换
}

static void __irq ADCHandle()
{
int ADCValue;

ClearPending(BIT_ADC);

if(rSUBSRCPND & (1<<10))//判断是不是有ADC普通转换产生的中断
{
rSUBSRCPND &= (1<<10);
ADCValue = (rADCDAT0 & 0x3FF);//读取转换值

Uart_Printf("/n ADC convert value is %d/n", ADCValue);
rADCCON |= 1;//开始转换
}
}
六,触摸屏程序
流程:
初始化
1 设置采样延时和分频值ADCDLY ADCCON
2 中断相关设置
3 设置触摸屏AD转换为等待中断模式 ADCTSC
中断服务函数
4清相关挂起寄存器
5设置转换模式,一般为连续x,y转换
6 启动转换
7 转换完成后读取x y坐标ADCDAT0 ADCDAT1
8 设置触摸屏AD转换为等待中断模式,设置等待弹起中断ADCTSC
9 弹起中断发生后,设置触摸屏转换为等待中断模式,等待下一次触笔按下rADCTSC
程序
void TouchPendInit()
{
ADCInit();
rADCTSC = 0xd3;//设置为等待中断模式
rSUBSRCPND |= (1<<9);//清ADC_TC挂起寄存器
rINTSUBMSK &= ~(1<<9);//ADC_TC屏蔽
}
void ADCInit()
{
int channel = 0, preScaler = 50000000/ADCFRE - 1;

rADCDLY=50000; //设置触摸屏转换延时,这个延时需要设置,否则数值不对
//rADCCON = (channel<<3) | (preScaler<<6) | (1<<14);//设置ADC通道和预分频值
rADCCON=(1<<14) | (preScaler<<6);
Delay(100);

pISR_ADC = (U32)ADCHandle;//设置中断向量表
ClearPending(BIT_ADC);//清挂起寄存器
rSUBSRCPND |= (1<<10);//清子中断挂起寄存器
EnableIrq(BIT_ADC);//使能ADC中断,实际是关闭INTMSK对ADC的屏蔽
rINTSUBMSK &= ~(1<<10);//关闭子中断对ADC_S的屏蔽*/
}

static void __irq ADCHandle()
{
int xADCValue, yADCValue;

ClearPending(BIT_ADC);

if(rSUBSRCPND & (1<<9))
{
rSUBSRCPND |= (1<<9);
rADCTSC |= (1<<2);//设置转换模式,X,Y连续自动转换
rADCCON |= 1;//开始转换
}
if(rSUBSRCPND & (1<<10))//判断是不是有ADC普通转换产生的中断
{
rSUBSRCPND |= (1<<10);
xADCValue = (rADCDAT0 & 0x3FF);//读取转换值
yADCValue = (rADCDAT1 & 0x3FF);
Uart_Printf("/n x=%d, y=%d/n", xADCValue, yADCValue);
rADCTSC = 0xd3;//设置为等待中断模式
rADCTSC |= (1<<8);//检测弹起中断

//等待弹起
while(1)
{
if(rSUBSRCPND & (1<<9))
{
rSUBSRCPND |= (1<<9);
rADCTSC = 0xd3;
break;
}
}
}

}

七,LCD程序
流程:
初始化
1 端口初始化,设置相应的端口为LCD端口
2 显示模式初始化,设置帧同步信号,行同步信号,屏幕大小,像素显示模式等等
3 帧缓冲初始化,设置帧缓冲的起始地址,中止地址,虚拟屏宽等等
开始输出
4 启动输出
5 往帧缓冲写入要显示的图像数据
程序:

#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_PIXCLOCK 4

#define LCD_RIGHT_MARGIN 39
#define LCD_LEFT_MARGIN 16

#define LCD_HSYNC_LEN 5

#define LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN 1

static unsigned short lcdbuffer[LCD_WIDTH][LCD_HEIGHT];//定义显存
void LCDPortInit()
{
rGPCCON = 0xaaaa00a8;//设置GPC端口为VCLK,VLINE,VFRAME VD输出
rGPCUP = 0xffff;
rGPDCON = 0xaaaaaaaa;//设置GPD端口为VD输出
rGPDUP = 0xffff;//禁止上拉

}

void LCDInit()
{
U32 bufferaddr =(U32)lcdbuffer;
LCDPortInit();
rLCDCON1 = (LCD_PIXCLOCK<<8) | (3<<5) | (0xC<<1);//设置VCLK时钟,显示模式为TFT,像格式素模式为16位
rLCDCON2 = (LCD_UPPER_MARGIN<<24) | ((LCD_HEIGHT-1)<<14) | (LCD_LOWER_MARGIN<<6) | (LCD_VSYNC_LEN<<0);//设置帧同步信号发生后无效的时钟,LCD高度,下一帧同步信号发生前的无效时钟,行同步信号的秒冲宽度
rLCDCON3 = (LCD_LEFT_MARGIN<<19) | ((LCD_WIDTH-1)<<8) | (LCD_RIGHT_MARGIN<<0);//设置行同步信号发生后无效的时钟,LCD宽度,下一个同步行同步信号发生前的无效时钟宽度
rLCDCON4 = (LCD_HSYNC_LEN<<0);//设置行同步信号时钟脉冲宽度
rLCDCON5 = (1<<11);//设置像素格式为5:6::5
rLCDSADDR1 = ((bufferaddr>>22)<<21) | (((bufferaddr>>1)&0x1fffff)<<0);//设置显存的起始地址
rLCDSADDR2 = ((bufferaddr + LCD_WIDTH*LCD_WIDTH*2)>>1)&0x1ffff;//设置显存的末端地址
rLCDSADDR3 = LCD_WIDTH; //设置虚拟屏幕宽度即为实际宽度

rLCDCON1 |= 1; //开始输出
}

void LCDDisplay()
{

LCDSubDisplay((0x1f<<11) | (0x00<<5) | (0x00)); //red
delay(100000);
LCDSubDisplay((0x00<<11) | (0x3f<<5) | (0x00)); //green
delay(100000);
LCDSubDisplay((0x00<<11) | (0x00<<5) | (0x1f)); //blue
delay(100000);


}

void LCDSubDisplay(U16 c)
{
unsigned int x ,y ;

for( x=0; x for( y=0; y lcdbuffer[x][y] = c;
}

 

你可能感兴趣的:(ARM程序设计)