S3C2440电阻触摸屏驱动设计
最后更新于:2022-04-01 16:11:51
实验:当触笔点击在触摸屏上时,SecureCRT上显示触点的X,Y坐标;
目的:测试电阻触摸屏驱动程序;
开发板:S3C2440
~~~
/*
*版权所有(C)2015,ZJU
*
*文件名称:ts.c
*内容摘要:关于触摸屏的配置
*其它说明:开发板型号: TX2440
* 电阻屏
*当前版本:V1.0
*作 者:Frank
*完成日期:2015.12.22
*
*/
#define ADCCON (*(volatile unsigned int *)0x58000000) //ADC control register
#define ADCTSC (*(volatile unsigned int *)0x58000004) //ADC Touch Screen Control Register
#define ADCDLY (*(volatile unsigned int *)0x58000008) //ADC Start or interval delay register
#define ADCDATA0 (*(volatile unsigned int *)0x5800000C) //ADC conversion data register
#define ADCDATA1 (*(volatile unsigned int *)0x58000010) //ADC conversion data register
/*interrupt register*/
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTMSK (*(volatile unsigned long *)0x4A000008)
#define INTPND (*(volatile unsigned long *)0x4A000010)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)
#define INTSUBMSK (*(volatile unsigned long *)0x4A00001C)
int xdata=0;
int ydata=0;
/*******************************************************************
*函数名称:Ts_Init()
*功能描述:触摸屏工作初始化
*其他说明:配置ADC为触摸屏工作模式
*创建日期:2015.12.22
*******************************************************************/
void Ts_Init(void)
{
/*1、设置AD转换时钟*/
ADCCON = (1 << 14) | (49 << 6); //AD转换频率=PCLK/(Val + 1),此处设为 1MHz
/*2、设置中断屏蔽位*/
INTMSK = ~(1 << 31);
INTSUBMSK = ~(1 << 9);
/*3、进入等待中断的模式*/
ADCTSC = 0xd3;
/***清除CPSR寄存器中的第7位I(IRQ disable),因为在start.S中关闭了中断****/
//这一步的目的是开启总中断
__asm__(
"mrs r0, cpsr\n"
"bic r0, r0, #0x80\n"
"msr cpsr_c, r0\n"
:
:
);
}
/*******************************************************************
*函数名称:Ts_Handler()
*功能描述:触摸屏中断处理
*其他说明:按下触摸屏后的中断处理
*创建日期:2015.12.22
*******************************************************************/
void Ts_Handler(void)
{
/*1、启动XY坐标自动转换*/
ADCTSC = (1 << 2);
ADCCON |= (1 << 0);
/*2、等待转换完成*/
while(!(ADCCON & (1 << 15)));
/*3、获取坐标*/
xdata = ADCDATA0 & 0x3ff;
ydata = ADCDATA1 & 0x3ff;
/*4、清除按下中断*/
SUBSRCPND |= (1 << 9);
SRCPND = (1 << 31);
INTPND = (1 << 31);
/*5、进入等待弹起中断*/
ADCTSC = 0xd3;
ADCTSC |= (1 << 8);
while(1)
{
if (SUBSRCPND & (1 << 9))
break;
}
/*6、清除弹起中断*/
SUBSRCPND |= (1 << 9);
SRCPND = (1 << 31);
INTPND = (1 << 31);
//对读取到的X,Y坐标进行处理
printf("X is %d, Y is %d\n", xdata, ydata);
printf("\n\r");
/*7、再次进入等待按下中断的状态*/
ADCTSC = 0xd3;
}
~~~
中断处理文件interrupt.c中触摸屏中断函数的调用程序:
~~~
/*******************************************************************
*函数名称:handle_int()
*功能描述:中断处理函数
*其他说明:
*******************************************************************/
void handle_int()
{
/*判断产生中断的中断源*/
unsigned long value = *(INTOFFSET);
switch (value)
{
case 31 :
Ts_Handler(); //触摸屏中断
break;
default :
break;
}
}
~~~
linux中宿主目录、Vi和Vim关系
最后更新于:2022-04-01 16:11:48
1、宿主目录:所谓宿主目录,就是操作系统为当前用户所设计的用来存放文件、工作的默认目录。如windows中的“我的文档”,就是windows为我们设计的宿主目录。
linux中每个用户都有自己的宿主目录,这个目录对于普通用户来说,在home/username,而对于root用户来说,在/root。所以当用户为root时,~与/root等价。
2、为什么在linux中键入vi等价于键入vim?
vim是vi的升级版。其实现在使用vi不是真正的打开vi编辑器,现在的vi其实是一个link文件,指向的是vim,所以键入vi相当于键入vim。
3、vi的高级使用
①查找:在命令模式下,输入 /xxx,就可以查找到xxx;
②快速切换行:在命令模式下,输入:num,就可以快速切换到num行;
③设置显示行号:在命令模式下,输入:set nu,就可以显示行号;
④取消显示行号:在命令模式下,输入:set nonu,就可以取消显示行号。
S3C2440 LCD液晶模块驱动设计
最后更新于:2022-04-01 16:11:46
参数配置:
开发板型号TX2440; 一个像素点采用16位表示; LCD屏幕分辨率 480*272; 液晶模块型号:BL43014_SPEC;图像的内容以C语言数组的形式保存在bmp.c文件中。
~~~
/*
*版权所有(C)2015,ZJU
*
*文件名称:lcd.c
*内容摘要:关于lcd的配置
*其它说明:开发板型号: TX2440
* led显示尺寸 480 x 272
液晶模块型号:BL43014_SPEC
*当前版本:V1.0
*作 者:Frank
*完成日期:2015.12.20
*
*/
#define GPCCON (*(volatile unsigned long *)0x56000020)
#define GPDCON (*(volatile unsigned long *)0x56000030)
#define GPGCON (*(volatile unsigned long *)0x56000060)
//LCD控制寄存器定义
#define LCDCON1 (*(volatile unsigned long *)0x4D000000)
#define LCDCON2 (*(volatile unsigned long *)0x4D000004)
#define LCDCON3 (*(volatile unsigned long *)0x4D000008)
#define LCDCON4 (*(volatile unsigned long *)0x4D00000C)
#define LCDCON5 (*(volatile unsigned long *)0x4D000010)
#define LCDSADDR1 (*(volatile unsigned long *)0x4D000014)
#define LCDSADDR2 (*(volatile unsigned long *)0x4D000018)
#define LCDSADDR3 (*(volatile unsigned long *)0x4D00001C)
#define TPAL (*(volatile unsigned long *)0x4D000050)
/*以下宏定义的值参考所使用的LCD液晶模块的芯片手册*/
#define VSPW 9
#define VBPD 7
#define LINEVAL 271
#define VFPD 7
#define CLKVAL 4 // 10 = 100 / ((CLKVAL+1)*2)
#define HSPW 40
#define HBPD 39
#define HOZVAL 479
#define HFPD 4
unsigned short LCDBUFFER[272][480]; //定义一个LCD图像缓存空间
extern unsigned char bmp[90200];
typedef unsigned char U8;
typedef unsigned short int U16;
typedef unsigned int U32;
/*******************************************************************
*函数名称:Lcd_Port_Init()
*功能描述:LCD端口初始化
*其他说明:配置LCD用到的GPIO端口
*创建日期:2015.12.20
*******************************************************************/
void Lcd_Port_Init(void)
{
GPDCON = 0xaaaaaaaa;
GPCCON = 0xaaaaaaaa;
}
/*******************************************************************
*函数名称:Lcd_Control_Init()
*功能描述:LCD控制寄存器初始化
*其他说明:配置LCD用到的控制寄存器
*创建日期:2015.12.20
*******************************************************************/
void Lcd_Control_Init(void)
{
LCDCON1 = (CLKVAL << 8) | (0x3 << 5) | (0xC << 1) | (0 << 0);
LCDCON2 = (VBPD << 24) | (LINEVAL << 14) | (VFPD << 6) | (VSPW);
LCDCON3 = (HBPD << 19) | (HOZVAL << 8) | (HFPD);
LCDCON4 = HSPW;
// LCDCON5 = (1 << 11) | (1 << 9) | (1 << 8) | (1 << 0);
LCDCON5 = (1 << 11) | (1 << 9) | (1 << 8);
LCDSADDR1 = (((unsigned int)LCDBUFFER >> 22) << 21) | (((unsigned int)LCDBUFFER >> 1) & 0x1fffff);
LCDSADDR2 = (((unsigned int)LCDBUFFER + 272*480*2) >> 1) & 0x1fffff;
LCDSADDR3 = (0 << 11) | (480*2 / 2);
TPAL = 0;
}
/*******************************************************************
*函数名称:Lcd_Init()
*功能描述:LCD初始化
*其他说明:
*创建日期:2015.12.20
*******************************************************************/
void Lcd_Init()
{
Lcd_Port_Init();
Lcd_Control_Init();
/*打开LCD电源*/
GPGCON |= (0x3 << 8);
LCDCON5 |= (1 << 3);
/*使能LCD控制器*/
LCDCON1 |= 1;
}
/*******************************************************************
*函数名称:point()
*功能描述:在lcd上画一个点
*其他说明:
*创建日期:2015.12.20
*******************************************************************/
void Point(unsigned int x, unsigned int y, unsigned int color)
{
unsigned int red, green, blue;
red = (color >> 19) & 0x1f;
green = (color >> 10) & 0x3f;
blue = (color >> 3) & 0x1f;
/*采用5:6:5的模式表示一个RGB*/
LCDBUFFER[y][x] = (unsigned short)((red << 11) | (green << 5) | blue);
}
/*******************************************************************
*函数名称:Paint_Bmp(U16,U16,U16,U16,U8)
*功能描述:LCD显示一幅图像
*输入参数:(x0,y0)图像显示第一个点的坐标; high图像的高度
wide图像的宽度; bmp存储图像的数组
*其他说明:
*创建日期:2015.12.20
*******************************************************************/
void Paint_Bmp(U16 x0, U16 y0, U16 wide, U16 high, const U8 *bmp)
{
unsigned short x, y;
unsigned short c;
unsigned int p = 0;
for (y=y0; y<y0+high; y++)
{
for (x=x0; x<x0+wide; x++)
{
c = bmp[p] | (bmp[p+1]<<8); /*一个像素点由16位表示,bmp[p]
表示低8位,bmp[p+1]表示高8位*/
if ((x<480) && (y<272))
{
LCDBUFFER[y][x] = c;
}
p = p + 2;
}
}
}
/*******************************************************************
*函数名称:Lcd_Test()
*功能描述:LCD测试函数
*其他说明:
*创建日期:2015.12.20
*******************************************************************/
void Lcd_Test(void)
{
int x;
/* for (x=0; x<480; ++x)
{
Point(x++, 150, 0xff0000);
}
*/
Paint_Bmp(0,0,220,220,bmp);
}
~~~
需要注意的几点:
1、宏定义的值需要参考使用的LCD液晶模块的芯片手册(TX2440使用的LCD是BL43014_SPEC)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f9a7b54.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f9c733d.jpg)
图1 BL43014_SPEC Input Time Table
2、液晶模块的Vsync和Hsync的时序与S3C2440芯片手册上的相反,所以LCDCON5中bit8和bit9需要设置为1。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f9e6495.jpg)
图2 BL43014_SPEC Vertical Input Timing
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724fa0cc5e.jpg)
图3 TFT LCD Timing Example( S3C2440 Datasheet)
S3C2440 – DMA传输(以字符传输为例)
最后更新于:2022-04-01 16:11:44
## 一、使用DMA的优点及DMA支持的请求源
1、DMA优点是其进行数据传输时不需要CPU的干涉,可以大大提高CPU的工作效率。
2、DMA在大容量数据传输中非常重要,比如图像数据传输,SD卡数据传输,USB数据传输等。
3、S3C2440有四个DMA,每个DMA支持的工作方式基本相同,但支持的DMA请求源可能略有不同。如下为四个DMA通道分别支持的DMA请求源:
Ch0: nXDREQ0, UART0, SDI, Timer, USB EP1
Ch1: nXDREQ1, UART1, I2SSDI, SPI0, USB EP2
Ch2: I2SSDO, I2SSDI, SDI, Timer, USB EP3
Ch3: UART1, SDI, SPI1, Timer, USB EP4
那么怎么使用DMA呢,S3C2440内部集成了DMA控制器,我们只需要简单的配置一下寄存器就可以实现DMA的传输了。
## 二、DMA配置步骤及要点: (参考[点击打开链接](http://blog.csdn.net/cybertan/article/details/4697516))
1.数据从哪里来,到哪里去?
使用DMA首先我们要知道数据的流向,DISRCx寄存器是DMA初始源寄存器,存放了数据的源地址。DIDSTx是DMA的初始目的寄存器,存放数据的目的地址。
2.数据走的什么总线?地址是否是固定的?
我们还要知道源与目的数据存储设备在什么总线上(AHB系统总线,一般是高速的比如内存,APB外围总线,低速的比如SD,UART;具体走什么总线可以在datasheet上查到);以及数据传输结束以后起始地址还原到发送前的起始地址呢,还是在现在的末尾+1做为新的起始地址。这些设置在DISRCCx与DIDSTCx两个寄存器里面配置。
3.数据以什么方式传输?源与目的是什么设备?要不要自动重载?
需要确定数据的传输方式有请求还是握手,根据上面的总线确定与什么时钟同步(HCLK,PCLK),是单元传输还是突发传输,是以字节传输还是字传输,是否重载。是单服务(只发送一次)还是多服务(不停循环发送),以及数据的传送大小。选择源与目的设备。最后还要确定中断是不是传输结束发生(CURR_TC记数是不是0)。这些都在DCONx中设置。
4.怎么开始传输DMA和停止DMA,这些在DMASKTRIG中设置。
## 三、操作实例(以DMA传输一段字符到控制台为例)
~~~
/*
*版权所有(C)2015,ZJU
*
*文件名称:dma.c
*内容摘要:关于DMA的配置
*其它说明:开发板型号: TX2440
* 注意Uart_Init中,设置UCON0寄存器时TX的方式要设置为DMA模式
*当前版本:V1.0
*作 者:Frank
*完成日期:2015.12.19
*
*/
#define DISRC0 (*(volatile unsigned long *)0x4B000000) //DMA 0 initial source register
#define DISRCC0 (*(volatile unsigned long *)0x4B000004) //DMA 0 initial source control register
#define DIDST0 (*(volatile unsigned long *)0x4B000008) //DMA 0 initial destination register
#define DIDSTC0 (*(volatile unsigned long *)0x4B00000C) //DMA 0 initial destination control register
#define DCON0 (*(volatile unsigned long *)0x4B000010) //DMA 0 control register
#define DMASKTRIG0 (*(volatile unsigned long *)0x4B000020) //DMA 0 mask trigger register
#define UTXH0 (volatile unsigned long *)0x50000020 //UART channel 0 transmit buffer register
char *buf = "Hello World!";
/*******************************************************************
*函数名称:Dma_Init()
*功能描述:DMA初始化函数
*其他说明:
*创建日期:2015.12.19
*******************************************************************/
void Dma_Init(void)
{
//初始化源地址
DISRC0 = (unsigned int)buf;
DISRCC0 = (0 << 1) | (0 << 0); //DMA起始地址在AHB总线(Memory), 地址递增
//初始化目的地址
DIDST0 = (unsigned int)UTXH0; //目的地址为 UTXH0
DIDSTC0 = (1 << 1) | (1 << 0); /*DMA目的地址在APB总线(UART0), 地址不
变(因为要发送的字符总是传到UTXH0)*/
DCON0 = (1 << 24) | (1 << 23) | (1 << 22) | (12 << 0); //设置DMA请求源及工作模式
}
/*******************************************************************
*函数名称:Dma_Start()
*功能描述:DMA启动函数
*其他说明:
*创建日期:2015.12.19
*******************************************************************/
void Dma_Start(void)
{
DMASKTRIG0 = (1 << 1); //启动DMA通道
}
~~~
需要注意的几点是:
①没有使用DMA中断;
②使用的是Demand模式,实测发现Handshake模式也是可以的(推荐);
③在Uart_Init中,要将UCON0寄存器设置为发送采用DMA模式(开始就是没有设置,所以一直无法看到实验现象);
④源地址和目的地址要使用unsigned int强制转换为整数放进寄存器中;
UART裸机驱动设计
最后更新于:2022-04-01 16:11:42
UART裸机驱动设计主要在于配置各寄存器的值;
开发板型号: TX2440;
UART 使能: 使能UART0;
传 输 参 数: 8位数据位,1位停止位,无奇偶校验,波特率115200;
时 钟 参 数: UART使用系统的 PCLK.
~~~
/*******************************************************************
*版权所有(C)2015,ZJU
*
*文件名称:uart.c
*内容摘要:关于UART的配置
*其它说明:开发板型号:TX2440
* UART0:RXD0 -- GPH3 , TXD0 -- GPH2
*当前版本:V1.0
*作 者:Frank
*完成日期:2015.12.14
*
*******************************************************************/
#define GPHCON (*(volatile unsigned long *)0x56000070) //Configures the pins of port H
#define ULCON0 (*(volatile unsigned long *)0x50000000) //UART channel 0 line control register
#define UCON0 (*(volatile unsigned long *)0x50000004) //UART channel 0 control register
#define UTRSTAT0 (*(volatile unsigned long *)0x50000010) //UART channel 0 Tx/Rx status register
#define UTXH0 (*(volatile unsigned long *)0x50000020) //UART channel 0 transmit buffer register
#define URXH0 (*(volatile unsigned long *)0x50000024) //UART channel 0 receive buffer register
#define UBRDIV0 (*(volatile unsigned long *)0x50000028) //Baud rate divisior register 0
#define PCLK 50000000 //PCLK=50M (start.S中设置了 FCLK:HCLK:PCLK = 1:4:8, FCLK=400M)
#define BAUD 115200
/*******************************************************************
*函数名称:Uart_Init()
*功能描述:UART初始化函数
*其他说明:
*创建日期:2015.12.14
*******************************************************************/
void Uart_Init(void)
{
/*配置引脚功能*/
GPHCON &= (~(0xf << 4));
GPHCON |= (0xa << 4); //GPH2设为TXD0,GPH3设为RXD0
/*设置数据格式*/
ULCON0 = 0b11; //8位数据位,1位停止位,无奇偶校验
/*设置工作模式*/
UCON0 = 0b0101; //发送和接收均采用轮询方式
/*设置波特率*/
UBRDIV0 = (int)(PCLK/(BAUD*16) - 1); //波特率设置为115200
}
/*******************************************************************
*函数名称:Puts()
*功能描述:字符发送函数
*输入形参:ch
*返 回 值:无
*其他说明:ch为需要发送的字符
*创建日期:2015.12.14
*******************************************************************/
void Puts(unsigned char ch)
{
while (!(UTRSTAT0 & (1 << 2)));
UTXH0 = ch;
}
/*******************************************************************
*函数名称:Gets()
*功能描述:字符接收函数
*输入形参:无
*返 回 值:ch
*其他说明:ch为接收到的字符
*创建日期:2015.12.14
*******************************************************************/
unsigned char Gets(void)
{
char ch;
while (!(UTRSTAT0 & (1 << 0)));
ch = URXH0;
return ch;
}
~~~
Nand Flash裸机驱动设计
最后更新于:2022-04-01 16:11:39
Nand Flash型号 : K9F2G08U0A
## 1、以页方式读取Nand Flash中的数据
①选中Nand Flash芯片;
②清除RnB;
③发送命令0x00;
④发送列地址(列地址两个周期,行地址三个周期);
⑤发送行地址(页号);
⑥发送命令0x30;
⑦等待RnB;
⑧读取RnB;
⑨取消选中Nand Flash芯片;
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f8e5b4f.jpg)
## 2、以页方式向Nand Flash中写入数据
①选中Nand Flash芯片;
②清除RnB;
③发送命令0x80;
④发送列地址;发送行地址;
⑤向Nand Flash写入数据;
⑥发送命令0x10;
⑦等待RnB;
⑧发送命令0x70;
⑨读取写入状态;
⑩取消选中Nand Flash;
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f9301f2.jpg)
## 3、块擦除操作
①选中Nand Flash芯片;
②清除RnB;
③发送命令0x60;
④发送行地址;
⑤发送命令0xD0;
⑥等待RnB;
⑦读取擦除状态;
⑧取消选中Nand Flash;
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f9501b2.jpg)
## 4、K9F2G08U0A的阵列组织(即内部组成结构)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f9746b3.jpg)
## 5、在srart.S中,需要调用Nand_Init函数对Nand Flash进行初始化,然后调用Nand_To_Ram函数将Nand Flash中的数据复制到SDRAM中。
~~~
/*******************************************************************
*版权所有(C)2015,ZJU
*
*文件名称:nand.c
*内容摘要:关于NAND flash的配置
*其它说明:NAND flash型号: K9F2G08U0B
* 开发板型号: TX2440
*当前版本:V1.0
*作 者:Frank
*完成日期:2015.12.12
*
*******************************************************************/
#define NFCONF (*(volatile unsigned long *)0x4E000000) //NAND flash configuration register
#define NFCONT (*(volatile unsigned long *)0x4E000004) //NAND flash control register
#define NFCMMD (*(volatile unsigned char *)0x4E000008) //NAND flash command set register
#define NFADDR (*(volatile unsigned char *)0x4E00000C) //NAND flash address register
#define NFDATA (*(volatile unsigned char *)0x4E000010) //NAND flash data register
#define NFSTAT (*(volatile unsigned char *)0x4E000020) //NAND flash operation status register
#define TACLS 1
#define TWRPH0 2
#define TWRPH1 1
/*******************************************************************
*函数名称:Select_Chip()
*功能描述:NAND flash使能函数
*其他说明:选中NAND flash(片选使能)
*创建日期:2015.12.12
*******************************************************************/
void Select_Chip(void)
{
NFCONT &= ~(1 << 1);
}
/*******************************************************************
*函数名称:Deselect_Chip()
*功能描述:NAND flash禁止函数
*其他说明:取消选中NAND flash(片选禁止)
*创建日期:2015.12.12
*******************************************************************/
void Deselect_Chip(void)
{
NFCONT |= (1 << 1);
}
/*******************************************************************
*函数名称:Wait_RnB()
*功能描述:等待RnB
*其他说明:RnB为1时表示响应了
*创建日期:2015.12.12
*******************************************************************/
void Wait_RnB(void)
{
while (!(NFSTAT & (1 << 2)));
}
/*******************************************************************
*函数名称:Clear_RnB()
*功能描述:清除RnB
*其他说明:
*创建日期:2015.12.12
*******************************************************************/
void Clear_RnB(void)
{
NFSTAT |= (1 << 2);
}
/*******************************************************************
*函数名称:Send_Cmd(unsigned char cmd)
*功能描述:NAND flash命令发送
*输入参数:cmd
*返 回 值:无
*其他说明:cmd为向NAND flash发送的命令字符
*创建日期:2015.12.12
*******************************************************************/
void Send_Cmd(unsigned char cmd)
{
NFCMMD = cmd;
}
/*******************************************************************
*函数名称:Send_Addr(unsigned char addr)
*功能描述:NAND flash地址发送函数
*输入参数:addr
*返 回 值:无
*其他说明:addr为8位地址
*创建日期:2015.12.12
*******************************************************************/
void Send_Addr(unsigned char addr)
{
NFADDR = addr;
}
/*******************************************************************
*函数名称:NF_PageRead(unsigned long addr, unsigned char * buff)
*功能描述:读取NAND flash中整页的内容
*输入参数:addr, *buff
*返 回 值:无
*其他说明:addr为所读取页的行地址,buff寄存器存储读取的NF中的内容
*创建日期:2015.12.12
*******************************************************************/
void NF_PageRead(unsigned long addr, unsigned char * buff)
{
int i = 0;
Select_Chip(); //选中NAND flash芯片
Clear_RnB(); //清除RnB
Send_Cmd(0x00); //发送命令0x00
Send_Addr(0x00); //发送列地址(列地址两个周期,行地址三个周期)
Send_Addr(0x00);
Send_Addr(addr & 0xff); //发送行地址(页号)
Send_Addr((addr >> 8) & 0xff);
Send_Addr((addr >> 16) & 0xff);
Send_Cmd(0x30); //发送命令0x30
Wait_RnB(); //等待RnB
for (i=0; i<2048; ++i) //读取数据
{
buff[i] = NFDATA;
}
Deselect_Chip(); //取消选中NAND flash芯片
}
/*******************************************************************
*函数名称:NF_PageWrite(unsigned long addr, unsigned char * buff)
*功能描述:以页的方式向NAND flash中写入数据
*输入参数:addr, *buff
*返 回 值:ret
*其他说明:addr为所写页的行地址,buff寄存器为向NF中写入的内容
*创建日期:2015.12.13
*******************************************************************/
int NF_PageWrite(unsigned long addr, unsigned char * buff)
{
int ret ;
int i = 0;
Select_Chip(); //选中NAND flash芯片
Clear_RnB(); //清除RnB
Send_Cmd(0x80); //发送命令0x80
Send_Addr(0x00); //发送列地址
Send_Addr(0x00);
Send_Addr(addr & 0xff); //发送行地址
Send_Addr((addr >> 8) & 0xff);
Send_Addr((addr >> 16) & 0xff);
for (i=0; i<2048; ++i) //向NAND flash写入数据
{
NFDATA = buff[i];
}
Send_Cmd(0x10); //发送命令0x10
Wait_RnB(); //等待RnB
Send_Cmd(0x70); //发送命令0x70
ret = NFDATA; //读取写入状态
Deselect_Chip(); //取消选中NAND flash
return ret;
}
/*******************************************************************
*函数名称:NF_Erase(unsigned long addr)
*功能描述:整块擦除NAND flash
*输入参数:addr
*返 回 值:ret
*其他说明:addr为所擦除页的行地址,ret为擦除状态返回值
*创建日期:2015.12.13
*******************************************************************/
int NF_Erase(unsigned long addr)
{
int ret;
Select_Chip(); //选中NAND flash芯片
Clear_RnB(); //清除RnB
Send_Cmd(0x60); //发送命令0x60
Send_Addr(addr & 0xff); //发送行地址
Send_Addr((addr >> 8) & 0xff);
Send_Addr((addr >> 16) & 0xff);
Send_Cmd(0xD0); //发送命令0xD0
Wait_RnB(); //等待RnB
Send_Cmd(0x70); //发送命令0x70
ret = NFDATA; //读取擦除状态
Deselect_Chip(); //取消选中NAND flash芯片
return ret;
}
/*******************************************************************
*函数名称:Nand_Reset()
*功能描述:复位NAND flash
*其他说明:
*创建日期:2015.12.12
*******************************************************************/
void Nand_Reset(void)
{
Select_Chip(); //选中NAND flash
Clear_RnB(); //清除RnB
Send_Cmd(0xff); //发送0xff命令
Wait_RnB(); //等待RnB
Deselect_Chip(); //取消选中NAND flash
}
/*******************************************************************
*函数名称:Nand_Init()
*功能描述:初始化NAND flash
*其他说明:
*创建日期:2015.12.12
*******************************************************************/
void Nand_Init(void)
{
NFCONF = ((TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4)); //初始化NFCONF
NFCONT = ((1 << 0) | (1 << 1)); //初始化NFCONT
Nand_Reset(); //复位
}
/*******************************************************************
*函数名称:Nand_To_Ram(unsigned long , unsigned char * , int)
*功能描述:读取NAND flash中整页的内容
*输入参数:start_addr, * sdram_addr, size
*返 回 值:无
*其他说明:addr为所读取页的行地址,buff寄存器存储读取的NF中的内容
*创建日期:2015.12.12
*******************************************************************/
void Nand_To_Ram(unsigned long start_addr, unsigned char * sdram_addr, int size)
{
int i = 0;
for (i=(start_addr>>11); size>0; ) //采用的是按页拷贝的方式,不需要列地址,只保留行地址
{
NF_PageRead(i, sdram_addr);
size -= 2048;
sdram_addr += 2048;
i++;
}
}
~~~
对((volatile unsigned long ))的理解
最后更新于:2022-04-01 16:11:37
最近在学习Nand Flash驱动程序设计时,涉及到((volatile unsigned long )),通过查阅部分网络文章,对其用法和指针有了进一步理解,遂作此笔记。本文参考了yueleilei的文章--ARM定义特殊寄存器(*(volatile unsigned long *))的理解[点击打开链接](http://bbs.ednchina.com/BLOG_ARTICLE_3002274.HTM),在此感谢。
## 一、对 #define SREG (*(volatile unsigned char *)0x5F)的理解
嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,比如0x5F,第一步是要把它强制转换为指针类型(unsigned char *)0x5F,AVR的SREG是八位寄存器,所以0x5F强制转换为指向unsigned char类型。
(注意,这一步的理解很关键,很多文章说:( volatile unsigned long * )的意思是将后面的那个地址强制转换成 volatile unsigned long * ,当初看文章一直不理解这句话,其实解释为:将(volatile unsigned long * )后面跟的内容转化成一个指针,并且该指针指向一个易变的无符号整数,这样更易理解。)
volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到。
第二步,对指针变量引用,就能操作指针所指向的地址的内容了
*(volatile unsigned CHAR *)0x5F
第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯,所以
#define SREG (*(volatile unsigned CHAR *)0x5F)
类似的,如果使用一个32位处理器,要对一个32位的内存地址进行访问,可以这样定义
#define RAM_ADDR (*(volatile unsigned LONG *)0x0000555F)
然后就可以用C语言对这个内存地址进行读写操作了
读:tmp = RAM_ADDR;
写:RAM_ADDR = 0x55;
## 二、举例
例:如何理解(*(volatile unsigned long *)0x56000010)
解答:
long是32位整型,unsigned指无符号数,左边的*表示取内容;
volatile表示易变的,告诉编译器不要优化,这个地址的内容不一定是在程序中改变的(可能是硬件上改变的,如寄存器中某些状态位的改变);
(volatile unsigned long *) 表示将后面跟的内容转化成一个指针,并且是指向一个易变的无符号整数。左边再加个,表示取该指针指向地址的内容。
总的意思是取那个内存单元(内存地址0x56000010)里存的数,并将这个数转化为无符号整数。
嵌入式学习路线与方法的一个不错的讲座
最后更新于:2022-04-01 16:11:35
## 一、嵌入式开发用到哪些知识
1、编程语言: 第一层次(C语言),第二层次(ARM汇编、shell脚本、C++、Java);
2、操作系统: 第一层次(linux),第二层次(Android、RTOS(一类实时操作系统,如ucos、uclinux等,一般用在需要操作系统的单片机上));
3、硬件方面: 会看原理图,懂得串口、I2C、USB等常见通信设备的原理及编程、懂得LCD、触摸屏等常见元件原理及编程。
## 二、嵌入式开发需要什么技能
1、以软件为主,但是需要对硬件有一定了解;
2、重在调试,需要有修改和调试程序的能力;
## 三、学习体系
1、嵌入式核心课程
2、Android扩展
3、物联网扩展
4、各种实训项目
## 总结:
①嵌入式是处理“软硬结合”部分的;
②嵌入式软件开发并不需要会设计硬件电路和自己画PCB板;
③嵌入式软件开发以C语言和linux系统为核心,学习的关键和入口点就在这里;
④嵌入式开发涉及面极广,学习周期长,学习难度大。
## 四、嵌入式核心课程(5大模块)
1、linux基础 + C语言;
2、linux应用程序开发(系统编程、网络编程);
3、ARM编程(ARM体系架构、ARM汇编、开发板常见外设裸机编程);
4、linux系统移植(uboot移植、kernel移植、rootfs制作);
5、linux驱动开发。
## 五、Android扩展(3大模块)
1、Android HAL驱动;
2、Android系统定制;
3、Android应用开发;
## 六、物联网扩展(无线扩展)
1、GPS和地图查询服务;
2、wifi;
3、蓝牙4.0;
4、Zigbee;
5、RFID.
讲座内容来自朱有鹏老师的 《嵌入式工程师养成计划--嵌入式软件工程师完全学习指南》,在此感谢^_^
S3C2440 外部按键中断解析
最后更新于:2022-04-01 16:11:33
学习bootloader制作的过程中,学到 “通过按键进入中断控制LED亮灭”的实验时,自己所用的开发板和视频讲解中的不同,于是琢磨了一下中断涉及到的各个寄存器,并进行编码尝试,最终完成了实验,达到了通过按键以中断方式控制LED亮灭的目的。2440属于非向量中断方式,和6410、210的向量中断方式对于中断的处理有一些不同,因此本文的讲解并不完全适合6410和210。
## 一、概念
中断分为两大类:外部中断和内部中断。
1、外部中断:S3C2440的24个外部中断占用GPF0~GPF7(EINT0~EINT7)、 GPG0~GPG15(EINT8~EINT23)。使用这些引脚作为中断输入时,必须将引脚配置为EINT模式,配置方法可参考datasheet。
2、内部中断:内部中断包括DMA中断、UART中断、IIC中断等等由内部外设触发的中断。
3、相关寄存器:S3C2440中断控制涉及到10个寄存器-- SRCPND、INTMOD、INTMSK、PRIORITY、INTPND、INTOFFSET、SUBSRCPND、INTSUBMSK、EINTMASK、EINTPEND,这10个寄存器的定义及其功能描述如下表一。对中断的控制无外乎配置和处理这10个寄存器中的某几个。
表一 与S3C2440中断相关的10个寄存器的信息
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f870aeb.jpg)
4、各寄存器的关系(工作流程)。根据中断源进行分类,中断的处理流程可用下图1表示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f89867b.jpg)
图1 S3C2440中断处理流程图
由上图1可知,S3C2440的中断可分为四种情况:由外部中断源(EINT0~EINT3)触发的中断、由外部中断子中断源(EINT4~EINT23)触发的中断、由内部中断源(内部子中断)触发的中断和由内部中断源(非子中断)触发的中断。
5、中断的开启(中断初始化,INTMOD 和 PRTORITY使用默认值)
(a)如果是外部中断(EINT0~EINT3)和内部中断(不带子中断),需设置INTMSK,让它不屏蔽中断即可;
(b)如果是带子中断的内部中断,需设置INTSUBMSK 和 INTMSK,让它们不屏蔽中断即可;
(c)如果是外部中断(EINT4~EINT23),需设置EINTMASK 和 INTMSK,让它们不屏蔽中断即可;
注意:CPSR中的第7位I也需清除(在start.S中关闭了中断,这一步是针对在bootloader设计中进行EINT实验时的操作)
6、中断处理流程
(a)如果是外部中断(EINT0~EINT3)和 不带子中断的内部中断,发生中断后SRCPND相应位置1,如果没有被 INTMSK屏蔽,那么等待进一步处理;
(b)如果是带子中断的内部中断,发生中断后SUBSRCPND相应位置1,如果没有被INTSUBMSK屏蔽,则SRCPND相应位置1,如果没有被INTMSK屏蔽,那么等待进一步处理;
(c)如果是外部中断(EINT4~EINT23),发生中断后EINTPEND相应位置1,如果没有被EINTMASK屏蔽,则SRCPND相应位EINT4-7和EINT8~23置1,如果没有被INTMSK屏蔽,那么等待进一步处理;
三种中断都等待进一步处理了,接下来从SRCPND继续往前看,看看INTMSK,如果中断被屏蔽了,就不用说了(注意:快中断也能被屏蔽)。如果没有被屏蔽,那么会进一步到INTMOD。如果是快中断,那么直接出来,进入FIQ(即CPU进入快中断模式处理)。如果是普通中断,那么SRCPND可以有多位置1(FIQ只能有一个),这时就会经过PRIORITY选出一个优先级高的,然后根据选出的中断把INTPND相应位置1(注意:只能选出一个),进入IRQ,让CPU处理。
INTOFFSET寄存器用来表示INTPND中哪一位置1了,可以用来判断请求中断的中断源,但是,对于外部中断EINT4~EINT23是无法判断的,如EINT4~EINT7中任何一个中断源请求中断,都会将INTOFFSET中的位EINT4_7置1,所以要判断具体是哪个中断源请求的中断,可读取EINTPEND中的值进行判断。
7、中断的清除
(a)如果是外部中断EINT0~EINT3和不带子中断的内部中断,只需清除SRCPND(注意:清除时对相应位写“1”);
(b)如果是带子中断的内部中断,需清除SRCPND和SUBSRCPND,注意先清除SUBSRCPND,再清除SRCPND。因为如果先清除SRCPND的话,在清除SUBSRCPND的过程中,SRCPND会以为又有中断发生,又会置1,也就是说一次中断会响应两次,所以必须先掐断源头,对它们同样是写“1”清除
(c)如果是外部中断EINT4~EINT23,需清除EINTPEND和SRCPND(同样注意顺序),写“1”清除。
## 二、按键中断实验
1、所使用开发板的按键与芯片连接示意如下图2,其中KEY1 --> GPF4, KEY2--> GPF5,KEY3 --> GPF6,KEY4 --> GPF7。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_579724f8b6162.jpg)
图2 按键与S3C2440连接示意
通过查阅s3c2440的datasheet可知,GPF4~GPF7对应外部中断EINT4~EINT7。所以对于中断初始化,需设置寄存器EINTMASK和INTMSK,取消中断屏蔽。中断处理过程中,可通过阅读EINTPEND寄存器的值判定触发中断的中断源。中断清除时,向EINTPEND和SRCPND中相应位写“1”,清除中断(注意顺序)。以下为实验的源代码:
~~~
/*interrupt registes*/
#define SRCPND (volatile unsigned long *)0x4a000000 //Interrupt request status
#define INTMOD (volatile unsigned long *)0x4a000004 //Interrupt mode control
#define INTMSK (volatile unsigned long *)0x4a000008 //Interrupt mask control
#define PRIORITY (volatile unsigned long *)0x4a00000c //IRQ priority control
#define INTPND (volatile unsigned long *)0x4a000010 //Interrupt request status
#define INTOFFSET (volatile unsigned long *)0x4A000014 //Interrupt request source offset
#define SUBSRCPND (volatile unsigned long *)0x4A000018 //Sub source pending
#define INTSUBMSK (volatile unsigned long *)0x4A00001c //Interrupt sub mask
#define EINTMASK (volatile unsigned long *)0x560000a4 //External interrupt mask register
#define EINTPEND (volatile unsigned long *)0x560000a8 //External interrupt pending register
/*******************************************************************
*函数名称:init_irq()
*功能描述:中断初始化(取消中断屏蔽)
*其他说明:按键中断对应的是外部中断EINT4~7,所以设置EINTMASK、INTMSK
*修改日期 版本号 修改人 修改内容
*------------------------------------------------------------------
*2015.12.5 V1.0
*******************************************************************/
void init_irq()
{
*(EINTMASK) &= ((~(1 << 4)) & (~(1 << 5)) & (~(1 << 6)) & (~(1 << 7))); //取消EINT4~7的子中断屏蔽(注意,这步必须在后面一步之前)
*(INTMSK) &= (~(1 << 4)); //取消EINT4~7的中断屏蔽
/***清除CPSR寄存器中的第7位I(IRQ disable),因为在start.S中关闭了中断****/
__asm__(
"mrs r0, cpsr\n"
"bic r0, r0, #0x80\n"
"msr cpsr_c, r0\n"
:
:
);
}
/*******************************************************************
*函数名称:handle_int()
*功能描述:中断处理函数
*其他说明:
*修改日期 版本号 修改人 修改内容
*------------------------------------------------------------------
*2015.12.6 V1.0
*******************************************************************/
void handle_int()
{
/*判断产生中断的中断源*/
unsigned long value = *(INTOFFSET);
unsigned long value_offset = *(EINTPEND); //value_offset的值分别对应EINT4~EINT7
if (4 == value) //value = 4 表明EINT4 ~ EINT7请求中断 (这个条件不是必须的,可直接判断value_offset)
{
switch (value_offset)
{
case 0x00000010 : //EINT4 --> k1
led_on();
break;
case 0x00000020 : //EINT5 --> k2
led_on();
break;
case 0x00000040 : //EINT6 --> k3
led_off();
break;
case 0x00000080 : //EINT7 --> k4
led_off();
break;
default :
break;
}
}
/*清除中断标志*/
*(EINTPEND) = value_offset; //把EINTPEND中当前值重新赋给EINTPEND,即相当于对相应位置1清除(不过这
//样做的前提是可以明确只发生一个中断,不然有可能将其他的中断请求清除)
*(INTPND) = 1 << 4;
}
~~~
参考文章:http://blog.chinaunix.net/uid-25100840-id-351208.html
前言
最后更新于:2022-04-01 16:11:30
> 原文出处:[嵌入式linux与ARM学习](http://blog.csdn.net/column/details/luckylinux.html)
作者:[lnf_2373837248](http://blog.csdn.net/lnf_2373837248)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# 嵌入式linux与ARM学习
> linux与ARM学习中的一些技术心得和笔记,部分文章也涉及单片机、电子电路等知识。技术是在交流中不断进步的,各位看官,谬误之处不吝赐教,以助小弟成长!^_^