实验(10)、ADC_TS
实验目的:通过电压量的数字化采样,获取触摸屏位置
(1)head.S程序如下:
@******************************************************************************
@ File: head.S
@功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@******************************************************************************
.extern main
.text
.global _start
_start:
@******************************************************************************
@中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************
b Reset
@ 0x04:未定义指令中止模式的向量地址
HandleUndef:
b HandleUndef
@ 0x08:管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
b HandleSWI
@ 0x0c:指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort
@ 0x10:数据访问终止导致的异常的向量地址
HandleDataAbort:
b HandleDataAbort
@ 0x14:保留
HandleNotUsed:
b HandleNotUsed
@ 0x18:中断模式的向量地址
b HandleIRQ
@ 0x1c:快中断模式的向量地址
HandleFIQ:
b HandleFIQ
Reset:
ldr sp, =4096 @设置栈指针,以下都是C函数,调用前需要设好栈
bl disable_watch_dog @关闭WATCHDOG,否则CPU会不断重启
bl clock_init @设置MPLL,改变FCLK、HCLK、PCLK
bl memsetup @设置存储控制器以使用SDRAM
bl nand_init @初始化NAND Flash
@复制代码到SDRAM中
ldr r0, =0x30000000 @ 1.目标地址= 0x30000000,这是SDRAM的起始地址
mov r1, #4096 @ 2.源地址= 4096,在SDRAM中运行的代码保存在NF该址开始处
mov r2, #16*1024 @ 3.复制长度= 16K,对于本实验,这是足够了
bl CopyCode2SDRAM @调用C函数CopyCode2SDRAM
@将NAND Flash中地址4096开始的2048字节代码(main.c编译得到)复制到SDRAM中
bl RdNF2SDRAM @调用C函数RdNF2SDRAM
bl clean_bss @清除bss段,未初始化或初值为0的全局/静态变量保存在bss段
msr cpsr_c, #0xd2 @进入中断模式
ldr sp, =0x31000000 @设置中断模式栈指针
msr cpsr_c, #0xdf @进入系统模式
ldr sp, =0x34000000 @设置系统模式栈指针
ldr lr, =ret_initirq @设置返回地址
ldr pc, =init_irq @调用中断初始化函数
ret_initirq:
msr cpsr_c, #0x5f @设置I-bit=0,开IRQ中断
ldr lr, =halt_loop @设置返回地址
ldr pc, =main @调用main函数
halt_loop:
b halt_loop
HandleIRQ:
sub lr, lr, #4 @计算返回地址
stmdb sp!,{ r0-r12,lr } @保存使用到的寄存器
@注意,此时的sp是中断模式的sp
@初始值是上面设置的4096
ldr lr, =int_return @设置调用IRQ_Handle函数后的返回地址
ldr pc, =IRQ_Handle @调用中断分发函数,在interrupt.c中
int_return:
ldmia sp!,{ r0-r12,pc }^ @中断返回, ^表示将spsr的值复制到cpsr
(2)init.c程序如下:
/*
* init.c:进行一些初始化
*/
#include "s3c24xx.h"
void disable_watch_dog(void);
void clock_init(void);
void memsetup(void);
void copy_steppingstone_to_sdram(void);
void clean_bss(void);
/*
*关闭WATCHDOG,否则CPU会不断重启
*/
void disable_watch_dog(void)
{
WTCON = 0; //关闭WATCHDOG很简单,往这个寄存器写0即可
}
#define FCLK 200000000
#define HCLK 100000000
#define PCLK 50000000
#define S3C2410_MPLL_200MHZ ((0x5c<<12)|(0x04<<4)|(0x00))
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
/*
*对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV
*有如下计算公式:
* S3C2410: MPLL(FCLK) = (m * Fin)/(p * 2^s)
* S3C2410: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)
*其中: m = MDIV + 8, p = PDIV + 2, s = SDIV
*对于本开发板,Fin = 12MHz
*设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:2:4,
* FCLK=200MHz,HCLK=100MHz,PCLK=50MHz
*/
void clock_init(void)
{
// LOCKTIME = 0x00ffffff; //使用默认值即可
CLKDIVN = 0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
/* HDIVN非0,CPU的总线模式应变为“asynchronous bus mode”*/
__asm__(
"mrc p15, 0, r1, c1, c0, 0\n" /*读出控制寄存器*/
"orr r1, r1, #0xc0000000\n" /*设置为“asynchronous bus mode”*/
"mcr p15, 0, r1, c1, c0, 0\n" /*写入控制寄存器*/
);
/*判断是S3C2410还是S3C2440 */
if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
{
MPLLCON = S3C2410_MPLL_200MHZ; /*现在FCLK=200M,HCLK=100M,PCLK=50M*/
}
else
{
MPLLCON = S3C2440_MPLL_200MHZ; /*现在FCLK=200M,HCLK=100M,PCLK=50M*/
}
}
/*
*设置存储控制器以使用SDRAM
*/
void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
/*这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值
*写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
* SDRAM之前就可以在steppingstone中运行
*/
/*存储控制器13个寄存器的值*/
p[0] = 0x22011110; //BWSCON
p[1] = 0x00000700; //BANKCON0
p[2] = 0x00000700; //BANKCON1
p[3] = 0x00000700; //BANKCON2
p[4] = 0x00000700; //BANKCON3
p[5] = 0x00000700; //BANKCON4
p[6] = 0x00000700; //BANKCON5
p[7] = 0x00018005; //BANKCON6
p[8] = 0x00018005; //BANKCON7
/* REFRESH,
* HCLK=12MHz: 0x008C07A3,
* HCLK=100MHz: 0x008C04F4
*/
p[9] = 0x008C04F4;
p[10] = 0x000000B1; //BANKSIZE
p[11] = 0x00000030; //MRSRB6
p[12] = 0x00000030; //MRSRB7
}
void copy_steppingstone_to_sdram(void)
{
unsigned int *pdwSrc = (unsigned int *)0;
unsigned int *pdwDest = (unsigned int *)0x30000000;
while (pdwSrc < (unsigned int *)4096)
{
*pdwDest = *pdwSrc;
pdwDest++;
pdwSrc++;
}
}
void clean_bss(void)
{
extern int __bss_start, __bss_end;
int *p = &__bss_start;
for (; p <&__bss_end; p++)
*p = 0;
}
(3)interrupt.c程序如下:
#include "s3c24xx.h"
void (*isr_handle_array[50])(void);
void Dummy_isr(void)
{
while(1);
}
void init_irq(void)
{
int i = 0;
for (i = 0; i < sizeof(isr_handle_array) / sizeof(isr_handle_array[0]); i++)
{
isr_handle_array[i] = Dummy_isr;
}
INTMOD = 0x0; //所有中断都设为IRQ模式
INTMSK = BIT_ALLMSK; //先屏蔽所有中断
// isr_handle_array[ISR_IIC_OFT] = I2CIntHandle;
}
void IRQ_Handle(void)
{
unsigned long oft = INTOFFSET;
//清中断
if (oft == 4)
EINTPEND = 1<<7; //EINT4-7合用IRQ4,注意EINTPEND[3:0]保留未用,向这些位写入1可能导致未知结果
SRCPND = 1<<oft;
INTPND = INTPND;
/*调用中断服务程序*/
isr_handle_array[oft]();
}
(3_2)interrupt.h程序如下:
void EINT_Handle();
(4)main.c程序如下:
#include <stdio.h>
#include "serial.h"
#include "adc_ts.h"
int main()
{
char c;
uart0_init(); //波特率115200,8N1(8个数据位,无校验位,1个停止位)
while (1)
{
printf("\r\n##### Test ADC and Touch Screem #####\r\n");
printf("[A] Test ADC\n\r");
printf("[T] Test Touch Screem\n\r");
printf("Enter your selection: ");
c = getc();
printf("%c\n\r", c);
switch (c)
{
case 'a':
case 'A':
{
Test_Adc();
break;
}
case 't':
case 'T':
{
Test_Ts();
break;
}
default:
break;
}
}
return 0;
}
(5)adc_ts.c程序如下:
/*
* FILE: adc_ts.c
* ADC和触摸屏的测试函数
*/
#include <stdio.h>
#include "adc_ts.h"
#include "s3c24xx.h"
#include "serial.h"
// ADCCON寄存器
#define PRESCALE_DIS (0 << 14)
#define PRESCALE_EN (1 << 14)
#define PRSCVL(x) ((x) << 6)
#define ADC_INPUT(x) ((x) << 3)
#define ADC_START(1 << 0)
#define ADC_ENDCVT (1 << 15)
// ADCTSC寄存器
#define UD_SEN (1 << 8)
#define DOWN_INT (UD_SEN*0)
#define UP_INT (UD_SEN*1)
#define YM_SEN (1 << 7)
#define YM_HIZ (YM_SEN*0)
#define YM_GND(YM_SEN*1)
#define YP_SEN (1 << 6)
#define YP_EXTVLT (YP_SEN*0)
#define YP_AIN (YP_SEN*1)
#define XM_SEN (1 << 5)
#define XM_HIZ (XM_SEN*0)
#define XM_GND (XM_SEN*1)
#define XP_SEN (1 << 4)
#define XP_EXTVLT (XP_SEN*0)
#define XP_AIN (XP_SEN*1)
#define XP_PULL_UP (1 << 3)
#define XP_PULL_UP_EN (XP_PULL_UP*0)
#define XP_PULL_UP_DIS (XP_PULL_UP*1)
#define AUTO_PST (1 << 2)
#define CONVERT_MAN (AUTO_PST*0)
#define CONVERT_AUTO (AUTO_PST*1)
#define XP_PST(x) (x << 0)
#define NOP_MODE 0
#define X_AXIS_MODE 1
#define Y_AXIS_MODE 2
#define WAIT_INT_MODE 3
/*设置进入等待中断模式,XP_PU,XP_Dis,XM_Dis,YP_Dis,YM_En
* (1)对于S3C2410,位[8]只能为0,所以只能使用下面的wait_down_int,
*它既等待Pen Down中断,也等待Pen Up中断
* (2)对于S3C2440,位[8]为0、1时分别表示等待Pen Down中断或Pen Up中断
*/
/*进入"等待中断模式",等待触摸屏被按下*/
#define wait_down_int() { ADCTSC = DOWN_INT | XP_PULL_UP_EN | \
XP_AIN | XM_HIZ | YP_AIN | YM_GND | \
XP_PST(WAIT_INT_MODE); }
/*进入"等待中断模式",等待触摸屏被松开*/
#define wait_up_int() { ADCTSC = UP_INT | XP_PULL_UP_EN | XP_AIN | XM_HIZ | \
YP_AIN | YM_GND | XP_PST(WAIT_INT_MODE); }
/*进入自动(连续) X/Y轴坐标转换模式*/
#define mode_auto_xy() { ADCTSC = CONVERT_AUTO | XP_PULL_UP_DIS | \
XP_PST(NOP_MODE); }
extern void (*isr_handle_array[50])(void);
/*
*使用查询方式读取A/D转换值
*输入参数:
* ch:模拟信号通道,取值为0~7
*/
static int ReadAdc(int ch)
{
//选择模拟通道,使能预分频功能,设置A/D转换器的时钟= PCLK/(49+1)
ADCCON = PRESCALE_EN | PRSCVL(49) | ADC_INPUT(ch);
//清除位[2],设为普通转换模式
ADCTSC &= ~(1<<2);
//设置位[0]为1,启动A/D转换
ADCCON |= ADC_START;
//当A/D转换真正开始时,位[0]会自动清0
while (ADCCON & ADC_START);
//检测位[15],当它为1时表示转换结束
while (!(ADCCON & ADC_ENDCVT));
//读取数据
return (ADCDAT0 & 0x3ff);
}
void wait(unsigned int dly)
{ for(; dly > 0; dly--);
}
/*
*测试ADC
*通过A/D转换,测量可变电阻器的电压值
*/
void Test_Adc(void)
{
float vol2;
int t2;
printf("Measuring the voltage of AIN2, press any key to exit\n\r");
while (!awaitkey(0)) //串口无输入,则不断测试
{
vol2 = (float)ReadAdc(2);
wait(200000);
vol2 += (float)ReadAdc(2);
wait(200000);
vol2 += (float)ReadAdc(2);
wait(200000);
vol2 += (float)ReadAdc(2);
vol2 = ((vol2/4.0)*3.3)/1024.0; //计算电压值
t2 = (vol2 - (int)vol2) * 1000; //计算小数部分,本代码中的printf无法打印浮点数
printf("AIN2 = %d.%-3dV\r", (int)vol2, t2);
}
printf("\n");
}
/*
* INT_TC的中断服务程序
*当触摸屏被按下时,进入自动(连续) X/Y轴坐标转换模式;
*当触摸屏被松开时,进入等待中断模式,再次等待INT_TC中断
*/
static void Isr_Tc(void)
{
if (ADCDAT0 & 0x8000)
{
printf("Stylus Up!!\n\r");
wait_down_int(); /*进入"等待中断模式",等待触摸屏被按下*/
}
else
{
printf("Stylus Down: ");
mode_auto_xy(); /*进入自动(连续) X/Y轴坐标转换模式*/
/*设置位[0]为1,启动A/D转换
*注意:ADCDLY为50000,PCLK = 50MHz,
*要经过(1/50MHz)*50000=1ms之后才开始转换X坐标
*再经过1ms之后才开始转换Y坐标
*/
ADCCON |= ADC_START;
}
//清INT_TC中断
SUBSRCPND |= BIT_SUB_TC;
SRCPND |= BIT_ADC;
INTPND |= BIT_ADC;
}
/*
* INT_ADC的中断服务程序
* A/D转换结束时发生此中断
*先读取X、Y坐标值,再进入等待中断模式
*/
static void Isr_Adc(void)
{
//打印X、Y坐标值
printf("xdata = %4d, ydata = %4d\r\n", (int)(ADCDAT0 & 0x3ff), (int)(ADCDAT1 & 0x3ff));
/*判断是S3C2410还是S3C2440 */
if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
{ // S3C2410
wait_down_int(); /*进入"等待中断模式",等待触摸屏被松开*/
}
else
{ // S3C2440
wait_up_int(); /*进入"等待中断模式",等待触摸屏被松开*/
}
//清INT_ADC中断
SUBSRCPND |= BIT_SUB_ADC;
SRCPND |= BIT_ADC;
INTPND |= BIT_ADC;
}
/*
* ADC、触摸屏的中断服务程序
*对于INT_TC、INT_ADC中断,分别调用它们的处理程序
*/
void AdcTsIntHandle(void)
{
if (SUBSRCPND & BIT_SUB_TC)
Isr_Tc();
if (SUBSRCPND & BIT_SUB_ADC)
Isr_Adc();
}
/*
*测试触摸屏,打印触点坐标
*/
void Test_Ts(void)
{
isr_handle_array[ISR_ADC_OFT] = AdcTsIntHandle; //设置ADC中断服务程序
INTMSK &= ~BIT_ADC; //开启ADC总中断
INTSUBMSK &= ~(BIT_SUB_TC); //开启INT_TC中断,即触摸屏被按下或松开时产生中断
INTSUBMSK &= ~(BIT_SUB_ADC); //开启INT_ADC中断,即A/D转换结束时产生中断
//使能预分频功能,设置A/D转换器的时钟= PCLK/(49+1)
ADCCON = PRESCALE_EN | PRSCVL(49);
/*采样延时时间= (1/3.6864M)*50000 = 13.56ms
*即按下触摸屏后,再过13.56ms才采样
*/
ADCDLY = 50000;
wait_down_int(); /*进入"等待中断模式",等待触摸屏被按下*/
printf("Touch the screem to test, press any key to exit\n\r");
getc();
//屏蔽ADC中断
INTSUBMSK |= BIT_SUB_TC;
INTSUBMSK |= BIT_SUB_ADC;
INTMSK |= BIT_ADC;
}
(6)framebuffer.c程序如下:
(7)s3c24xx.h程序如下:
/* WOTCH DOG register */
#define WTCON (*(volatile unsigned long *)0x53000000)
/* SDRAM regisers */
#define MEM_CTL_BASE 0x48000000
#define SDRAM_BASE 0x30000000
/* NAND Flash registers */
#define NFCONF (*(volatile unsigned int *)0x4e000000)
#define NFCMD (*(volatile unsigned char *)0x4e000004)
#define NFADDR (*(volatile unsigned char *)0x4e000008)
#define NFDATA (*(volatile unsigned char *)0x4e00000c)
#define NFSTAT (*(volatile unsigned char *)0x4e000010)
/*GPIO registers*/
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#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 GPHCON (*(volatile unsigned long *)0x56000070)
#define GPHDAT (*(volatile unsigned long *)0x56000074)
#define GPHUP (*(volatile unsigned long *)0x56000078)
/*UART registers*/
#define ULCON0 (*(volatile unsigned long *)0x50000000)
#define UCON0 (*(volatile unsigned long *)0x50000004)
#define UFCON0 (*(volatile unsigned long *)0x50000008)
#define UMCON0 (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
#define UTXH0 (*(volatile unsigned char *)0x50000020)
#define URXH0 (*(volatile unsigned char *)0x50000024)
#define UBRDIV0 (*(volatile unsigned long *)0x50000028)
/*interrupt registes*/
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTMOD (*(volatile unsigned long *)0x4A000004)
#define INTMSK (*(volatile unsigned long *)0x4A000008)
#define PRIORITY (*(volatile unsigned long *)0x4A00000c)
#define INTPND (*(volatile unsigned long *)0x4A000010)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)
#define INTSUBMSK (*(volatile unsigned long *)0x4A00001c)
/*external interrupt registers*/
#define EINTMASK (*(volatile unsigned long *)0x560000a4)
#define EINTPEND (*(volatile unsigned long *)0x560000a8)
(8)Makefile程序如下:
CC = arm-linux-gcc
LD = arm-linux-ld
AR = arm-linux-ar
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
INCLUDEDIR := $(shell pwd)/include
CFLAGS := -Wall -O2
CPPFLAGS :=-nostdlib-nostdinc -I$(INCLUDEDIR)
LDFLASG := -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
export CC LD OBJCOPY OBJDUMP INCLUDEDIR CFLAGS CPPFLAGS
objs := head.o init.o nand.o interrupt.o adc_ts.o serial.o main.o lib/libc.a
adc_ts.bin: $(objs)
${LD} -Tadc_ts.lds -o adc_ts_elf $^ ${LDFLASG}
${OBJCOPY} -O binary -S adc_ts_elf $@
${OBJDUMP} -D -m arm adc_ts_elf > adc_ts.dis
.PHONY : lib/libc.a
lib/libc.a:
cd lib; make; cd ..
%.o:%.c
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean:
make clean -C lib
rm -f adc_ts.bin adc_ts_elf adc_ts.dis *.o