使用SPI控制器,查询方式 来操作oled flash adc
adc.c文件
-
#include "adc.h"
-
#include "uart.h"
-
#include "string.h"
-
-
/*
-
*功能:AD转换初始化
-
*/
-
void init_ADC()
-
{
-
uart_sendString("init_ADC!\n");
-
//当PCLK为50M,ADC转换频率为2M时,算得预分频系数是24
-
unsigned int prscen = PCLK/ADC_FREQ -1;
-
-
/*ADCCON寄存器
-
*[15]:只读,AD转换结束标志 0:正在转换 1:转换结束
-
*[14]=1 1:AD转换器的时钟使用预分频 0:AD转换器的时钟不使用预分频
-
*[13:6]=19,预分频系数,因为初始这8是全1
-
*[5:3]=0b010,AD转换的通道
-
*[2] 0: 正常模式 1: 静态模式 初始是1
-
*[1] 0:读转换数据时不启动下一次转换 1:读转换数据时启动下一次转换
-
*[0] 当READ_START位=1时,这个位无效。 0:无作用 1:启动AD转换(当转换真正开始时,此位被请0)
-
*/
-
ADCCON &= (~(0xFF << 6)); //这里8位必须清0,
-
ADCCON &= (~(1 << 2));
-
//上面2句也可以合并成如下一句:
-
//ADCCON = 0x0; //全部清0,因为已经在前面保存了这个寄存器原来的值,所以这里可以全清0。
-
-
ADCCON |= ((1 << 14) | ((prscen & 0xff) << 6));
-
ADCCON &= (~(1 << 1));
-
ADC_DISABLE(); //初始化时,并不启动AD转换
-
-
//这里是普通的AD转换,ADCTSC寄存器的值,取默认值就行了。
-
ADCTSC &= (~(1<<2));
-
}
-
-
/*
-
*功能:选择进行AD转换的通道
-
*/
-
void get_ch(unsigned int chanal)
-
{
-
int i;
-
unsigned int sel_mux;
-
sel_mux = (ADCCON>>3)&0x7; //新通道之前的通道
-
-
uart_sendString("get_ch!\n");
-
if(sel_mux != chanal) //之前的通道,与现在选择的通道不同,则需要变换通道
-
{
-
ADCCON &= (~(7<<3)); //先把已有的数据清0
-
ADCCON |= (chanal<<3); //选择新的通道值。
-
//注意:选择新的通道值之后,需要延时一段时间,等待AD转换器反应过来
-
for(i=0; i<10000; i++);
-
for(i=0; i<10000; i++);
-
}
-
}
-
-
-
/*
-
*功能:读取AD转换后的值
-
*/
-
unsigned int read_ADC()
-
{
-
unsigned int data;
-
-
//启动AD转换
-
ADC_START();
-
-
uart_sendString("read_ADC!\n");
-
-
while(ADCCON & 0x1); //转换真正开始时,此位会被清0,清0之后继续向下走
-
-
while((ADCCON & 0x8000) == 0); //0:正在转换 1:转换结束,变成1之后继续向下走
-
-
data = (ADCDAT0 & 0x3ff); //ADCDAT0[9:0]10位,是普通AD转换后的数据值
-
-
return data;
-
}
-
-
-
-
/*
-
*函数功能 : 把0x0到0x3FF的数值,转化为对应的电压值
-
*注意 : 电源电压是 3.3V
-
* AD的精度是10位,即1024个等级
-
* 计算出来的电压值的小数位的精度:VOL_POINT=3,即保留3位有效小数位
-
*/
-
void adc_Voltage(unsigned int data, char * Voltage)
-
{
-
float vol; //用小数表示的电压值
-
unsigned int point; //电压值的小数部分
-
int i = 0;
-
unsigned int vol_prec = 10; //如果 VOL_POINT = 3,则这个值是1000;
-
-
for(i=0; i<VOL_POINT-1; i++) //有求幂函数的话,也可以使用。
-
{
-
vol_prec = vol_prec * 10;
-
}
-
-
vol = ((float)data * VCC)/ADC_PREC; //计算电压值
-
point = (vol - (unsigned int)vol) * vol_prec; //计算小数部分, *1000表示小树部分保留3位 本代码中的 sprintf uart_printf 无法打印浮点数
-
//uart_printf("AIN_2 = %d.%dV\r", (unsigned int)vol, point);
-
-
sprintf(Voltage,"%d.%dV", (unsigned int)vol, point);
-
}
adc.h文件
-
#ifndef __ADC_H__
-
#define __ADC_H__
-
-
/*模数转换相关寄存器*/
-
#define ADCCON (*(volatile unsigned long *)0x58000000)
-
#define ADCTSC (*(volatile unsigned long *)0x58000004)
-
#define ADCDAT0 (*(volatile unsigned long *)0x5800000C)
-
-
#define ADC_DISABLE() ((ADCCON) & (~(1<<0)))
-
#define ADC_START() ((ADCCON) |= (0x1))
-
-
//ADC转换频率最大为2M PCLK=50M
-
#define ADC_FREQ (2000000)
-
#define ADC_CHANAL_AIN2 2
-
-
void init_ADC();
-
void get_ch(unsigned int chanal);
-
unsigned int read_ADC();
-
void adc_Voltage(unsigned int data, char * Voltage);
-
-
#define ADC_WEI 10 //AD的精度是10位
-
#define ADC_PREC ((float)((1<<ADC_WEI)*(1.0))) //AD的精度是2^10=1024
-
#define VCC ((float)(3.3)) //电源电压值
-
#define VOL_POINT (3) //计算的电压值小数位的精度
-
-
#endif
def.h文件
-
#ifndef __DEF_H__
-
#define __DEF_H__
-
-
#define U32 unsigned int
-
#define U16 unsigned short
-
#define S32 int
-
#define S16 short int
-
#define U8 unsigned char
-
#define S8 char
-
-
#endif
flash.c文件
-
#include "string.h"
-
#include "uart.h"
-
-
#include "spi.h"
-
#include "oled.h"
-
#include "flash.h"
-
-
/*
-
具有spi接口的flash芯片
-
芯片手册 : W25Q16DV-具有SPI接口的flash芯片
-
FLASH W25X16
-
1, 工作电压:2.7 ~3.6 v ,工作电流0。5mA ,
-
2, 空间大小16Mbit, 2M Byte
-
3, 8192 个可编程页, 每页=256字节;
-
4, 用扇区擦除指令,每次可以擦除16页; 每个扇区=16页=4K字节; 整个flash共512个扇区
-
用块擦除指令每次可以擦除256页; 每个块=256页=64K字节; 整个flash共32个块
-
用整片擦除指令可以擦除整个芯片; 整个flash2M 字节
-
-
5, 用页编程指令,每次可编程256个字节;即使用页写命令,每次可写1页=256字节
-
*/
-
-
/*
-
*函数功能 : 选中flash的地址
-
*注意 : 我们使用的W25Q16DV芯片,flash内存是16Mbit=2M字节,共需要21根地址线来寻址
-
* 这里使用 3个字节的地址来表示flash芯片的地址,先发送3个字节地址的最高位,最后发送最低位
-
*/
-
void flash_select_addr(unsigned int addr)
-
{
-
spi_send_byte(addr >> 16); //这个函数会截取参数的低8位
-
spi_send_byte(addr >> 8);
-
spi_send_byte(addr & 0xff);
-
}
-
-
/*
-
*函数功能 : 设置 flash片选引脚 0:选中 1:不选中
-
*/
-
void flash_set_cs(unsigned char val)
-
{
-
if(val == 1) //让 OLED的命令|数据引脚 输出高电平
-
{
-
GPGDAT |= (1<<10); //GPG10输出高电平
-
}
-
else if(val == 0) //让 OLED的命令|数据引脚 输出低电平
-
{
-
GPGDAT &= (~(1<<10)); //GPG10输出低电平
-
}
-
}
-
-
/*
-
*函数功能 : 读取flash芯片的厂家ID,设备ID
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 52页
-
*/
-
void flash_read_id(unsigned char * mid, unsigned char * did)
-
{
-
unsigned char m_id;
-
unsigned char d_id;
-
-
flash_set_cs(0); //选中spi flash
-
-
spi_send_byte(0x90); //发出90命令
-
-
flash_select_addr(0); //选中flash的0地址
-
-
m_id = spi_recv_byte(); //第1次读取厂家ID
-
d_id = spi_recv_byte(); //第2次读取设备ID
-
-
*mid = m_id;
-
*did = d_id;
-
-
uart_printf("MID=[0x%x]\n", m_id); //0xEF
-
uart_printf("DID=[0x%x]\n", d_id); //0x14
-
-
flash_set_cs(1);
-
}
-
-
/*
-
*函数功能 : flash芯片写入使能
-
*函数参数 : enable 1 : 写使能 0 : 写禁止
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 23 24页
-
*/
-
void flash_write_enable(unsigned int enable)
-
{
-
if(enable == 1) //写使能
-
{
-
flash_set_cs(0); //选中spi flash
-
spi_send_byte(0x06); //发出06命令
-
flash_set_cs(1);
-
}
-
else if(enable == 0) //写禁止
-
{
-
flash_set_cs(0); //选中spi flash
-
spi_send_byte(0x04); //发出04命令
-
flash_set_cs(1);
-
}
-
}
-
-
/*
-
*函数功能 : 读取flash芯片的状态寄存器1
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25页
-
*/
-
unsigned char flash_read_status_reg1(void)
-
{
-
unsigned char val = 0;
-
-
flash_set_cs(0); //选中spi flash
-
spi_send_byte(0x05); //发出05命令
-
-
val = spi_recv_byte();
-
-
flash_set_cs(1);
-
-
return val;
-
}
-
-
/*
-
*函数功能 : 读取flash芯片的状态寄存器2
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25页
-
*/
-
unsigned char flash_read_status_reg2(void)
-
{
-
unsigned char val = 0;
-
-
flash_set_cs(0); //选中spi flash
-
spi_send_byte(0x35); //发出35命令
-
-
val = spi_recv_byte();
-
-
flash_set_cs(1);
-
-
return val;
-
}
-
-
/*
-
*函数功能 : flash初始化函数
-
*/
-
void flash_init(void)
-
{
-
unsigned char reg1_val;
-
-
reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值
-
-
flash_clean_protect_for_status_reg(); //去除状态寄存器的保护
-
flash_clean_protect_for_memory(); //去除整个存储区间的写保护
-
}
-
-
/*
-
*函数功能 : 去除状态寄存器的写保护,去保护之后,才可以写状态寄存器
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 15 16页
-
*工厂出厂默认状态是 : 写使能之后,SRP1=0,SRP0=0, 就可以写状态寄存器了。我们这里还是重新设置一下
-
*/
-
void flash_clean_protect_for_status_reg(void)
-
{
-
unsigned char reg1_val = 0;;
-
unsigned char reg2_val = 0;
-
-
reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值
-
reg2_val = flash_read_status_reg2(); //保存状态寄存器2中的值
-
-
//状态寄存器1 的bit7:SRP0 要清0
-
reg1_val &= (~(1<<7));
-
-
//状态寄存器2 的bit0:SRP1 要清0
-
reg2_val &= (~(1<<0));
-
-
flash_write_status_reg(reg1_val, reg2_val);
-
}
-
-
/*
-
*函数功能 : 去除整个存储区间的写保护,去保护之后,才可以写flash存储空间
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 16+17页
-
*CMP=0, BP2=0, BP1=0, BP0=0
-
*/
-
void flash_clean_protect_for_memory(void)
-
{
-
unsigned char reg1_val = 0;
-
unsigned char reg2_val = 0;
-
-
reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值
-
reg2_val = flash_read_status_reg2(); //保存状态寄存器2中的值
-
-
//状态寄存器1 的bit4:BP2 bit3:BP1, bit2:BP0 要清0
-
reg1_val &= (~(1<<4));
-
reg1_val &= (~(1<<3));
-
reg1_val &= (~(1<<2));
-
-
//状态寄存器2 的bit6:CMP 要清0
-
reg2_val &= (~(1<<6));
-
-
flash_write_status_reg(reg1_val, reg2_val);
-
}
-
-
/*
-
*函数功能 : 写flash芯片的状态寄存器
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25+26页
-
*注意 : 写状态寄存器之前,也要 先让flash芯片写使能 + 去除状态寄存器的保护 15页
-
* 工厂出厂默认状态是 : 写使能之后,SRP1=0,SRP0=0, 就可以写状态寄存器了。我们这里还是重新设置一下
-
*/
-
void flash_write_status_reg(unsigned char reg1_val, unsigned char reg2_val)
-
{
-
flash_write_enable(1); //flash芯片写使能
-
-
flash_set_cs(0); //选中spi flash
-
spi_send_byte(0x01); //发出01命令
-
-
spi_send_byte(reg1_val); //写寄存器1
-
spi_send_byte(reg2_val); //写寄存器2
-
-
flash_set_cs(1);
-
-
flash_wait_for_busy(); //等待写数据完成
-
}
-
-
/*
-
*函数功能 : flash擦除+写入时,需要一定处理时间,这里判断是否操作完成
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 16页
-
*注意 : 判断状态寄存器1的bit0:busy(0:内部操作完成,空闲 1:内部操作正在进行,忙)
-
*/
-
void flash_wait_for_busy(void)
-
{
-
unsigned char reg1_val;
-
-
while(1)
-
{
-
reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值,注意:这一句一定要放在循环里面,刚开始没有放在循环里面,排错排了很久
-
-
//uart_printf("****** reg1_val=[0x%x]*******\n", reg1_val);
-
-
if((reg1_val & 0x1) == 0) //等待bit0=0:内部操作完成
-
{
-
break;
-
}
-
}
-
//while((reg1_val & 0x1) == 1); //等待bit0=0,是1则一直循环
-
}
-
-
/*
-
*函数功能 : flash擦除1个扇区的内存空间
-
*函数参数 :
-
* addr : 将擦除的地址
-
* 1, 这个地址值,只有前21位有效
-
* 2, 因为是扇区擦除指令,所以一次最多擦除4K, 如果地址不是4K对齐,比如4094,则只擦除本扇区剩余的2个字节。绝对不会擦除接下来的扇区的空间
-
* 3, 地址最好4K对齐
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 43页
-
*注意 : 我们使用的W25Q16DV芯片,flash内存是16Mbit=2M字节,共需要21根地址线来寻址
-
* 用扇区擦除指令,每次可以擦除16页; 每个扇区=16页=4K字节; 整个flash共512个扇区
-
*/
-
void flash_erase_memory(unsigned int addr)
-
{
-
flash_write_enable(1); //flash芯片写使能
-
-
flash_set_cs(0); //选中spi flash
-
spi_send_byte(0x20); //发出20命令:扇区擦除指令
-
-
flash_select_addr(addr); //发出地址
-
-
flash_set_cs(1);
-
-
flash_wait_for_busy(); //等待擦除完成
-
}
-
-
/*
-
*函数功能 : 向flash存储空间写数据
-
*函数参数 :
-
* addr : 将写入的地址
-
* 1, 这个地址值,只有前21位有效
-
* 2, 因为是页编程指令,所以一次最多可写1页, 如果地址不是页对齐256字节,比如254,则只写本页剩余的2个字节。绝对不会连续写到下面的页
-
* 3, 写入的地址最好256字节对齐
-
* buf : 将写入的数据
-
* len : 将写入的数据的长度
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 41页
-
*/
-
void flash_write_memory(unsigned int addr, char * buf, int len)
-
{
-
int i = 0;
-
-
flash_write_enable(1); //flash芯片写使能
-
-
flash_set_cs(0); //选中spi flash
-
spi_send_byte(0x02); //发出02命令, 页编程指令,每次可编程256个字节;即使用页写命令,每次可写1页
-
-
flash_select_addr(addr); //发出地址
-
-
for(i=0; i<len; i++)
-
{
-
spi_send_byte(buf[i]); //一次写入一个字节的数据
-
}
-
-
flash_set_cs(1);
-
-
flash_wait_for_busy(); //等待写数据完成
-
}
-
-
/*
-
*函数功能 : 读取flash存储空间的数据
-
*函数参数 :
-
* addr : 将写入的地址
-
* buf : 将写入的数据
-
* len : 将写入的数据的长度
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 27页
-
*/
-
void flash_read_memory(unsigned int addr, char * buf, int len)
-
{
-
int i = 0;
-
-
flash_set_cs(0); //选中spi flash
-
spi_send_byte(0x03); //发出03命令
-
-
flash_select_addr(addr); //发出地址
-
-
for(i=0; i<len; i++)
-
{
-
buf[i] = spi_recv_byte(); //一次读取一个字节的数据
-
}
-
-
flash_set_cs(1);
-
}
flash.h文件
-
#ifndef _FLASH_H_
-
#define _FLASH_H_
-
-
void flash_read_id(unsigned char * mid, unsigned char * did);
-
void flash_select_addr(unsigned int addr);
-
void flash_set_cs(unsigned char val);
-
-
void flash_write_enable(unsigned int enable);
-
unsigned char flash_read_status_reg1(void);
-
unsigned char flash_read_status_reg2(void);
-
void flash_clean_protect_for_status_reg(void);
-
void flash_clean_protect_for_memory(void);
-
void flash_write_status_reg(unsigned char reg1_val, unsigned char reg2_val);
-
void flash_init(void);
-
-
void flash_wait_for_busy(void);
-
void flash_erase_memory(unsigned int addr);
-
void flash_write_memory(unsigned int addr, char * buf, int len);
-
void flash_read_memory(unsigned int addr, char * buf, int len);
-
-
#endif
head.S文件
-
@******************************************************************************
-
@ File: head.S head.o init.o nand.o interrupt.o uart.o touchscreen.o main.o
-
@ 功能: 设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
-
@******************************************************************************
-
.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
-
@******************************************************************************
-
@ 注意 : 代码的运行地址,并非代码的连接地址,所以上面的函数 disable_watch_dog clock_init memsetup nand_init
-
@ 上面的函数中,都不能有全局变量,因为全局变量和static变量一起,放在bss段中,大于4K地址范围了,bl是4K内可以跳转,所以函数使用全局变量会失败
-
@******************************************************************************
-
@ 复制nand中4k后的代码到SDRAM中
-
ldr r0, =_start @ 1. 目标地址 = 0x30000000,这是SDRAM的起始地址
-
mov r1, #0 @ 2. 源地址 = 0,代码保存在NAND Flash 0地址开始处
-
ldr r2, =__bss_start
-
sub r2, r2, r0 @ 3. 复制长度
-
bl CopyCode2SDRAM @ 调用C函数CopyCode2SDRAM
-
-
bl clean_bss @ 清除bss段,未初始化或初值为0的全局/静态变量保存在bss段
-
-
msr cpsr_c, #0xd2 @ 进入中断模式
-
ldr sp, =0x31000000 @ 设置中断模式栈指针
-
-
msr cpsr_c, #0xdf @ 进入系统模式
-
ldr sp, =0x34000000 @ 设置系统模式栈指针,
-
/*
-
ldr lr, =ret_inituart @ 设置返回地址
-
ldr pc, =init_uart @ 这里为什么不能用 bl init_uart ???,只能用ldr pc,也可以在main函数里面初始化
-
ret_inituart:
-
-
-
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 { r0-r12,lr } @ 保存使用到的寄存器
-
@ 注意,此时的sp是中断模式的sp
-
@ 初始值是上面设置的4096
-
-
ldr lr, =int_return @ 设置调用IRQ_Handle函数后的返回地址
-
ldr pc, =EINT_Handle @ 调用中断分发函数,在interrupt.c中
-
int_return:
-
ldmia { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr
init.c文件
-
#include "init.h"
-
-
/*
-
* 关闭WATCHDOG,否则CPU会不断重启
-
*/
-
void disable_watch_dog(void)
-
{
-
WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
-
}
-
-
/*
-
* 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV
-
* 有如下计算公式:
-
* S3C2440: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)
-
* 其中: m = MDIV + 8 = 92+8, p = PDIV + 2 = 1+2, s = SDIV = 2
-
* 对于本开发板,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的总线模式应该从“fast bus mode”变为“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" /* 写入控制寄存器 */
-
);
-
-
MPLLCON = S3C2440_MPLL_200MHZ;
-
/* 现在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz
-
并不保证你的任何设置都是合适的。所以,如果想要工作在200MHz,还是按照vivi的推荐值((0x5c<<12)|(0x01<<4)|(0x02))即可。
-
*/
-
}
-
-
/*
-
s3c2440的bank6和bank7支持128M B,注意,单位是 Byte.
-
TQ2440原理图的HY57V561620FTP是 4bank * 4M * 16bit,256M,注意,单位是 bit,也就是 32M Byte.
-
U6+U7 : 共64M
-
*/
-
void memsetup(void)
-
{
-
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
-
-
// 可生成”位置无关的代码”,使得这个函数可以在被复制到
-
// 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
-
}
-
-
/*
-
*清空bss段
-
*/
-
void clean_bss(void)
-
{
-
extern int __bss_start, __bss_end; /*这2个在链接脚本中定义的变量*/
-
int *p = &__bss_start;
-
-
for(; p < &__bss_end; p++)
-
{
-
*p = 0;
-
}
-
}
init.h文件
-
#ifndef _INIT_H_
-
#define _INIT_H_
-
-
//按键相关的GPIO
-
#define GPFCON (*(volatile unsigned long *)0x56000050)
-
#define GPFDAT (*(volatile unsigned long *)0x56000054)
-
#define GPFUP (*(volatile unsigned long *)0x56000058)
-
/*注意:这个程序没有mmu,所以寄存器的地址是物理地址*/
-
/**************************LED相关寄存器*******************************/
-
#define GPBCON (*(volatile unsigned long *)0x56000010)
-
#define GPBDAT (*(volatile unsigned long *)0x56000014)
-
#define GPBUP (*(volatile unsigned long *)0x56000018)
-
/*注意:这个程序没有mmu,所以寄存器的地址是物理地址*/
-
-
#define GPB5_OUT (1<<(5*2))
-
#define GPB6_OUT (1<<(6*2))
-
#define GPB7_OUT (1<<(7*2))
-
#define GPB8_OUT (1<<(8*2))
-
-
#define GPB5_ON (~(1<<5))
-
#define GPB6_ON (~(1<<6))
-
#define GPB7_ON (~(1<<7))
-
#define GPB8_ON (~(1<<8))
-
-
#define GPB5_OFF (1<<5)
-
#define GPB6_OFF (1<<6)
-
#define GPB7_OFF (1<<7)
-
#define GPB8_OFF (1<<8)
-
/*********************************************************/
-
-
/* WATCHDOG寄存器 */
-
#define WTCON (*(volatile unsigned long *)0x53000000)
-
/* SDRAM寄存器 */
-
#define MEM_CTL_BASE 0x48000000
-
#define SDRAM_BASE 0x30000000
-
#define GSTATUS1 (*(volatile unsigned long *)0x560000B0)
-
-
-
/*系统时钟相关寄存器*/
-
#define MPLLCON (*(volatile unsigned long *)0x4c000004)
-
#define CLKDIVN (*(volatile unsigned long *)0x4c000014)
-
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
-
-
void disable_watch_dog();
-
void clock_init(void);
-
void memsetup();
-
void clean_bss(void);
-
-
#endif
interrupt.c文件
-
#include "interrupt.h"
-
#include "uart.h"
-
#include "init.h"
-
-
/*定义一个函数指针数组 : 数组的每一个元素是一个函数指针,用来保存用户定义的中断处理函数*/
-
static void (*irq_interrupt_array[32])(void);
-
-
void init_irq_interrupt(void) /*没有安装中断函数的时候,使用这个函数来初始化中断服务函数*/
-
{
-
while(1);
-
}
-
-
/*函数功能:初始化函数指针数组
-
*/
-
void all_init_irq(void)
-
{
-
int i = 0;
-
for (i = 0; i < (sizeof(irq_interrupt_array) / sizeof(irq_interrupt_array[0])); i++)
-
{
-
irq_interrupt_array[i] = init_irq_interrupt;
-
}
-
}
-
-
/*
-
*函数功能:安装中断处理函数
-
*参数:
-
* num : 32个一级中断的编号,即中断号
-
* void (*irq_interrupt)(void) : 是个函数指针,是用户编写的中断处理函数。
-
*注意:
-
* 这个函数完整版是:用2个参数:1个表示1级中断,1个表示2级中断(没有2级中断时,用0)
-
*/
-
int request_irq(int num, void (*irq_interrupt)(void))
-
{
-
irq_interrupt_array[num] = irq_interrupt;
-
-
return 0;
-
}
-
-
/*
-
* 功能:初始化按键相关的GPIO引脚为外部中断
-
* 本程序:
-
* K1 : EINT1,引脚是GPF1
-
* K2 : EINT4,引脚是GPF4
-
* K3 : EINT2,引脚是GPF2
-
* K4 : EINT0,引脚是GPF0
-
*/
-
void init_irq(void)
-
{
-
GPFCON |= (GPF0_EINT | GPF1_EINT | GPF2_EINT | GPF4_EINT); /*设置这几个引脚为中断功能*/
-
GPFUP = 0x00;
-
-
EXTINT0 = 0x00; /*设置外部中断控制寄存器,低电平触发,默认也是0x00,所以这句可以不要*/
-
-
/*设置外部中断屏蔽寄存器,EINT4~EINT24,共20个需要在这个寄存器中使能他们,
-
*EINTMASK只能使能EINT4, EINT0,EINT1,EINT2,EINT3 不受EINTMASK控制
-
*EINTMASK[4]:禁止/使能EINT4
-
*/
-
EINTMASK &= (~(1<<4));
-
-
/*设置模式寄存器 :在IRQ模式中处理,默认也是0x00,所以这句可以不要*/
-
INTMOD = 0x00;
-
-
/*设置中断优先级寄存器
-
*仲裁租0和6,优先级不轮换使能,每个仲裁组下优先级顺序:0-1-2-3-4-5-6,默认也是这个顺序
-
*最终按键产生中断的优先级顺序:K4(EINT0) > K1(EINT1) > K3(EINT2) > K2(EINT4)
-
*/
-
PRIORITY &= (~(1<<0)) & (~(1<<6)) ;
-
-
/*设置中断屏蔽寄存器,一级中断
-
*INTMSK[0]:禁止/使能EINT0
-
*INTMSK[1]:禁止/使能EINT1
-
*INTMSK[2]:禁止/使能EINT2
-
*INTMSK[4]:禁止/使能EINT4~EINT7
-
*/
-
INTMSK &= (~(1<<0)) & (~(1<<1)) & (~(1<<2)) & (~(1<<4)); /*其实到这里,中断仍未完全开启,CPSR的I位还要清0*/
-
-
all_init_irq();
-
}
-
-
/*
-
*功能:中断服务子程序
-
*注意:1,按键产生中断的优先级顺序:K4(EINT0) > K1(EINT1) > K3(EINT2) > K2(EINT4)
-
* 所以,同时按下其中2个,3个或者4个,只有优先级最高的哪个LED会被点亮
-
* 2,如果硬件接线有LED连接了EINT4,EINT5,由于他们的中断优先级相同,EINTPEND[4:5]都是1,但都只会让SRCPND[4]=1,
-
* 如果他们同时按下,则INTPND[4]=1,且INTOFFSET=4,走到case 4,EINTPEND[4:5]=11b,2个if都成立,所以,最后这2个LED会被同时点亮。
-
*/
-
void EINT_Handle()
-
{
-
/*INTOFFSET:中断偏移寄存器,INTPND寄存器中的值被置1之后,中断偏移寄存器的值=32个一级中断的编号*/
-
unsigned long offset = INTOFFSET;
-
-
//uart_printf("\n\nINTOFFSET(interrupt no.)=[%d]\n", offset);
-
-
irq_interrupt_array[offset](); //调用对应的irq中断服务函数
-
-
/*中断处理完成之后:必须清除所有的挂起寄存器
-
*注意:向挂起寄存器中写1,即可令此位为0;写入0是没有效果的,若写入0,挂起寄存器中的数据保持不变。
-
*/
-
if(offset == 4) /*二级外部中断,还必须清除 EINTPEND*/
-
{
-
EINTPEND = (1<<4);
-
}
-
SRCPND = (1<<offset);
-
INTPND = (1<<offset);
-
-
//uart_printf("SRCPND=[0x%x]\n", SRCPND);
-
//uart_printf("INTPND=[0x%x]\n", INTPND);
-
//uart_sendString("*******************************************\n");
-
/*在清除 SRCPND INTPND时,INTOFFSET会被自动清除*/
-
}
interrupt.h文件
main.c文件
-
#include "def.h"
-
#include "init.h"
-
#include "nand.h"
-
#include "string.h"
-
#include "uart.h"
-
#include "setup.h"
-
#include "interrupt.h"
-
-
#include "spi.h"
-
#include "oled.h"
-
#include "flash.h"
-
#include "adc.h"
-
-
/*
-
本程序使用使用SPI控制器来控制spi的引脚,控制OLED
-
*/
-
-
int adc_test()
-
{
-
int i = 0;
-
unsigned int adc_data;
-
-
//这个目的是:保留之前的配置,本程序完之后,会恢复这个值。
-
//之所以保存它,是因为其他程序有可能使用了这个值,虽然本程序没有使用。
-
unsigned int ADCCON_save = ADCCON;
-
unsigned char str[32];
-
unsigned char Voltage[32];
-
unsigned char buf[32];
-
-
spi_cont_init(); //spi控制器的初始化
-
-
uart_printf("\n spi_cont_init ok\n");
-
oled_init(); //oled初始化
-
uart_printf("\n oled_init ok\n");
-
-
oled_print(0, 0, "oled_init ok");
-
-
//本实验仅完成模数转换,不涉及触摸屏,所以没有中断
-
uart_printf("\n ADC test:\n");
-
uart_printf("ADC frequency = 2MHZ\n");
-
uart_printf("ADC chanal = AIN 2\n");
-
uart_printf("\n begin:\n");
-
-
//AD转换初始化
-
init_ADC();
-
-
/*1,本程序流程:每按一次键,就读取一下AD转换的值,按ESC键结束本次测试。
-
* 2,不转动电位器时,每次读取的值可能不一样,但是差距会很小,这是正常的,每次转化会有误差,电位器电压有波动。
-
* 3,转动电位器,可以看见不同的点位值。
-
* 4,有硬件原理图可以看出:电位器电压范围是0~3.3V,这里可以转化成10位。1024个电压等级,每个等级电压差值=3.3V/1024
-
* 即0V时,数字量是0 3.3V时,数字量是1023。所以可以根据数字量计算出电压值。
-
*/
-
while(uart_getch() != 0x1b) //0x1b 是ESC键,没按键一次,读取一次AD转换后面的值
-
{
-
uart_printf("\n*****************************************\n");
-
//选择AD通道:本开发板硬件连线,只连接了AIN 2
-
get_ch(ADC_CHANAL_AIN2);
-
-
//读取AD转换后的值。
-
adc_data = read_ADC();
-
-
for(i=0; i<10000; i++);
-
for(i=0; i<10000; i++);
-
for(i=0; i<10000; i++);
-
for(i=0; i<10000; i++);
-
-
memset(str, 0, sizeof(str));
-
sprintf(str, "ADC_data = [0x%x]", adc_data);
-
uart_printf(str);
-
uart_printf("\n");
-
-
oled_clear();
-
oled_print(0, 0, str); //写到oled屏幕上
-
-
memset(Voltage, 0, sizeof(Voltage));
-
adc_Voltage(adc_data, Voltage);
-
-
memset(buf, 0, sizeof(buf));
-
sprintf(buf, "AIN_2 Voltage = [%s]", Voltage);
-
uart_printf(buf);
-
-
oled_print(4, 0, buf); //写到oled屏幕上
-
-
uart_printf("\n*****************************************\n");
-
}
-
ADCCON = ADCCON_save; //恢复保存的值
-
-
uart_sendString("end!\n");
-
-
return 0;
-
}
-
-
int main()
-
{
-
icache_enable();
-
/*注意 : 使用SPI控制器时,上面这一句加速的一定要屏蔽
-
否则会超过了最大使用频率
-
*/
-
-
init_irq();
-
init_uart();
-
-
uart_printf("\n\n");
-
uart_printf("My spitest start:\n\n");
-
-
adc_test();
-
-
return 0;
-
}
makefile文件
-
objs := head.o init.o setup.o nand.o main.o uart.o spi.o oled.o flash.o adc.o interrupt.o string.o
-
-
spi.bin: $(objs)
-
arm-linux-ld -Tspi.lds -o spi_elf $^ $(shell arm-linux-gcc -msoft-float -print-libgcc-file-name)
-
arm-linux-objcopy -O binary -S spi_elf $@
-
arm-linux-objdump -D -m arm spi_elf > spi.dis
-
-
%.o:%.c
-
arm-linux-gcc -Wall -O2 -c -o $@ $<
-
-
%.o:%.S
-
arm-linux-gcc -Wall -O2 -c -o $@ $<
-
-
clean:
-
rm -f spi.bin spi_elf spi.dis *.o
nand.c文件
-
#include "nand.h"
-
#include "def.h"
-
#include "uart.h"
-
-
/*
-
TQ2440开发板使用的nandflash芯片是 : K9F2G08U0A
-
*/
-
-
/*
-
TQ2440开发板使用的norflash芯片是EN29LV160AB-70TCP
-
*函数功能:判断是nand启动,还是nor启动
-
*返回值:0:nand启动,1:nor启动
-
*/
-
int isBootFromNorFlash(void)
-
{
-
volatile int *p = (volatile int *)0;
-
int val;
-
-
val = *p; /*先把0地址的值,保存到中间变量val*/
-
*p = 0x12345678; /*试图向0地址赋值*/
-
if(*p == 0x12345678) /*向0地址写数据成功*/
-
{
-
/* 写成功, 是nand启动 */
-
*p = val;
-
return 0;
-
}
-
else /*向0地址写数据失败*/
-
{
-
/* NOR不能像内存一样写 */
-
return 1;
-
}
-
}
-
-
/*
-
*功能:重启
-
*/
-
void nand_reset()
-
{
-
/*每操作NandFlash之前必须先片选*/
-
NF_CE_L();
-
/*发送命令之前要先清除RB*/
-
NF_CLEAR_RB();
-
/*向NandFlash写命令:即发送复位命令*/
-
NF_CMD(CMD_RESET);
-
/*注意:命令执行期间,NandFlash存储器忙,所以一直要等待为1:不忙*/
-
NF_WAIT_RB();
-
/*关闭片选*/
-
NF_CE_H();
-
}
-
-
/*
-
*功能:NANDFLASH初始化
-
*注意:1,本程序使用的是ARM9 2440处理器,需要设置 NFCONF,NFCONT这2个寄存器
-
* 2,NandFlash的芯片是K9F2G08U0A。由芯片手册可知:
-
* 容量:(256M+8M)字节,后面的8M叫OOB空间,坏块管理会用到它。
-
* 8bit
-
* 2K个块,每个块有64个page页
-
* 一个page页 =(2K+64)字节
-
* 所以,一个块 = 64页 = 64*(2K+64)字节 =(128K+2K)字节
-
* 一个Nand器件 = 2K个块 = 2K*64个page页 = 128K页
-
* 一个Nand器件 = 128K页 = 128K*(2K+64)字节 = (256M+8M)字节
-
* 3,片内内存+SDRAM+网卡+寄存器:是由CPU统一编址的,由CPU发出地址。
-
* 但是NandFlash不是由CPU统一编址的,所以NandFlash的0地址和片内内存的0地址,不是一回事。
-
* 4,NandFlash中的OOB不参与编址,读取NandFlash上的2049这个地址,不是读取的0页上的地址,是1页上的。
-
* 5,NandFlash控制器的各个控制引脚的时序,不需要程序员来驱动这些引脚,
-
* 程序员只需要设置相关寄存器之后,控制器会自动发出控制信号。
-
*/
-
void nand_init()
-
{
-
//char ret;
-
//char ch;
-
/*NANDFLASH配置寄存器 : NFCONF
-
[13:12]=01:时序图中CLE,ALE持续值设置,1个HCLK时钟周期,如HCLK是100Mhz,则,1个HCLK时钟周期=10ns。
-
[10:8]=011:时序图中TWRPH0持续值设置,3个HCLK时钟周期
-
[6:4]=100:时序图中TWRPH1持续值设置,0个HCLK时钟周期
-
*/
-
rNFCONF = ((TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4));
-
/*NANDFLASH控制寄存器 : NFCONT
-
其他位基本都是初始值或者不需要设置
-
[1]=1:禁止片选,CE引脚输出为1
-
[0]=1:MODE位,使能NandFlash控制器,命令使能CLE引脚设置为1
-
*/
-
//rNFCONT = (1<<4)|(1<<1)|(1<<0);
-
rNFCONT =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
-
//非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区(OOB区)ECC,使能nandflash片选及控制器
-
-
nand_reset();
-
-
/* 下面串口打印函数不能用了,因为连接脚本将nand.o放在4k内,uart.o在4k外,不能调用了
-
uart_sendByte('\n');
-
uart_sendByte('w');
-
uart_sendByte('x');
-
uart_sendByte('c');
-
uart_sendByte('\n');
-
-
ret = nand_readId();
-
uart_sendByte('\n');
-
uart_sendString("nand_readId = ");
-
uart_sendByte_hex(ret);
-
uart_sendByte('\n');
-
-
uart_sendString("please input:\n");
-
ch = uart_getch();
-
uart_sendString("your input is:");
-
uart_sendByte(ch);
-
uart_sendByte('\n');
-
*/
-
-
return ;
-
}
-
-
-
/*功能:读取芯片的ID号
-
*注意:查看NandFlash的相关配置是否正确,可以去检查芯片的ID号。如果ID读取正确了,则说明配置正确了。
-
*第1个周期:厂商编号 : 0xEC
-
*第2个周期:设备编号:相同型号的芯片,有相同的ID号 : 0xDA,这个必须正确
-
*第3个周期:内部芯片编号 : 0x10
-
*第4个周期:页的大小 : 0x95
-
*第5个周期:一个page另外带的融错空间的大小 : 0x44
-
*/
-
unsigned char nand_readId()
-
{
-
int i;
-
unsigned char cyc1;
-
unsigned char cyc2;
-
unsigned char cyc3;
-
unsigned char cyc4;
-
unsigned char cyc5;
-
//片选
-
NF_CE_L();
-
//清除RB
-
NF_CLEAR_RB();
-
//发送读ID命令
-
NF_CMD(CMD_READID);
-
//发送地址值:需要读取这个地址上的值,读ID号,规定就是这个地址,看的时序图,只需发送一个地址.
-
NF_ADDR(0x0);
-
for(i=0; i<1000; i++);
-
//从nandflash中读取数据:读5次,看时序图
-
//注意:每次只能读取8位数据,保存在数据寄存器中。
-
cyc1 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
cyc2 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
cyc3 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
cyc4 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
cyc5 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
-
//等待命令执行完毕
-
NF_WAIT_RB();
-
//关闭片选
-
NF_CE_H();
-
-
return cyc2;
-
}
-
-
-
/*功能 : 发出地址
-
*注意: 这个地址,不是页编号,是具体的读取NandFlash的起始地址,包括页内地址
-
*/
-
void write_addr(unsigned long addr)
-
{
-
//写地址(要读取数据的起始地址) :
-
//包括 : 列(页内)地址,是低位地址,共A0~A11,在具体的page页内寻址。一个page页=(2K+64)字节
-
// 行(页)地址,是高位地址,共A12~A28,可以寻址到page页。一个Nand器件=2K*64个page页 = 128K页
-
// 高位地址中,A12~A17,可以寻址到block块下面的page页。一个block块=64个page页
-
// 高位地址中,A18~A28,可以寻址到block块。一个Nand器件=2K个block块
-
//要写全部的5个周期的地址
-
-
//32位的page_number : 低29位有效 :
-
//| 28| 27| 26| 25| 24| 23| 22| 21| 20| 19| 18| 17| 16| 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
-
// 对应最后3个地址周期的页内地址的高11位 :
-
//|A28|A27|A26|A25|A24|A23|A22|A21|A20|A19|A18|A17|A16|A15|A14|A13|A12|A11|A10|A09|A08|A07|A06|A05|A04|A03|A02|A01|A00|
-
//29位在5个地址周期中应是如下的分布 :
-
//| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
-
//| L | L | L | L | L | L | L | A28 | //L表示低电平,即为0
-
//| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
-
//| A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 |
-
//| L | L | L | L | A11 | A10 | A9 | A8 |
-
//| A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
-
-
unsigned long i;
-
unsigned long col, page;
-
-
col = addr & (2048 -1); //截取低位 : 取2048的原因:本程序使用的nand芯片的一个page页是2048字节
-
page = addr / 2048; //截取高位 : 第page页,从第0页开始
-
-
//因为第11位已经丢弃了,所以A11肯定是0,所以这里其实只能寻址2047以内的页的main区,
-
//不可能寻址到2047以后的页内spare区,超过2047的地址,已经表示下一页的地址了。
-
NF_ADDR(col & 0xFF); //列(页内)地址 A0~A7
-
for(i=0; i<100; i++);
-
NF_ADDR((col >> 8) & 0x0F); //列(页内)地址 A8~A11
-
for(i=0; i<100; i++);
-
-
NF_ADDR(page & 0xFF); //行(页)地址 A12~A19
-
for(i=0; i<100; i++);
-
NF_ADDR((page >> 8) & 0xFF); //行(页)地址 A20~A27
-
for(i=0; i<100; i++);
-
NF_ADDR((page >> 16) & 0x01); //行(页)地址 A28
-
for(i=0; i<100; i++);
-
}
-
-
/*
-
*功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
-
* 数据方向:(Nand的start_addr->SDRAM的buf)
-
*参数 : buf : SDRAM的缓冲区起始地址:0x30001000
-
* start_addr : 如果要确保正确使用本函数来传输数据,这里的地址:不是页编号,但是必须每一个page页的开始地址,即页边界地址,即地址要2K对齐。
-
* size : 复制的字节数,最好是页空间整数倍。即使不指定为页的整数倍,也会读取2K整数倍的空间
-
*注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
-
* 2,如果在读取数据的过程中不进行ECC校验判断,则读操作比较简单,
-
* 在发出读命令的两个周期之间,发出要读取的页地址,然后读取数据即可
-
* 3,如果nand起始地址不是页对齐,则使用下面的nand_read2函数。
-
* 使用本函数,nand起始地址一定要页对齐
-
* 4,本函数的读操作是以页为单位进行的,size即使不是页的整数倍,也会读取整数倍的页数据。
-
* 5,如果这里的起始地址,非页对齐的地址,可能会导致我的程序重启(应该是读指令异常)
-
*/
-
void nand_read(unsigned char *buf, unsigned long start_addr, int size)
-
{
-
unsigned long i = 0, j = 0;
-
//1. 选中,片选
-
NF_CE_L();
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
//清除RB
-
NF_CLEAR_RB();
-
// 2. 发出读命令00h,读NandFlash时的第1个命令
-
//0x00命令发出后,即相当于向NFCMD寄存器中写入了0,nandflash控制器会自动发出各种控制信号,
-
//会自动满足时序图的要求,不再需要人工参与了。
-
//这样,nandflash控制器提供的几个寄存器,就简化了对nandflash的操作。
-
NF_CMD(CMD_READ1);
-
//3. 发出地址,写地址 (分5步发出)
-
write_addr(i);
-
//4. 发出读命令30h,读NandFlash时的第2个命令
-
NF_CMD(CMD_READ2);
-
//注意 : 因为这里没有继续发出页内随意读命令,所以当i=2048,2049的时候,
-
//上面发地址的函数write_addr(i);经过处理,已经跳过第0页了,是要读取第1页的第1,2个字节。
-
-
//5. 判断状态,等待NandFlash不忙
-
NF_WAIT_RB();
-
-
//6. 读数据 : 虽然读写过程可以不从页边界开始,但在这里必须从页边界开始读写至页结束
-
//注意 : uboot的命令 : nand dump 0 //从nand的0地址开始,1次读取1页的数据。
-
for(j=0; j<2048; j++, i++)
-
{
-
//1次从nandflash的8位IO口上读取8位数据。
-
//注意:每次读此寄存器,即执行一次下面语句,就会启动对nandflash的1次读操作。
-
//前一次读操作未完成,是不会启动下一次读操作的。
-
*buf = NF_RDDATA8();
-
buf++;
-
}
-
uart_sendString("#"); //2个nand read函数:目的是:main之后才初始化uart,可以使用这个带打印#的函数
-
}
-
//7. 取消选中,关闭片选
-
NF_CE_H();
-
}
-
-
-
/*
-
*功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
-
* 数据方向:(Nand的start_addr->SDRAM的buf)
-
*参数 : buf : SDRAM的缓冲区起始地址:0x30001000
-
* start_addr : 如果要确保正确使用本函数来传输数据,这里的地址:不是页编号,
-
* 但是必须每一个page页的开始地址,即页边界地址,即地址要2K对齐。
-
* size : 复制的字节数,最好是页空间整数倍。即使不指定为页的整数倍,也会读取2K整数倍的空间
-
*注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
-
* 2,如果在读取数据的过程中不进行ECC校验判断,则读操作比较简单,
-
* 在发出读命令的两个周期之间,发出要读取的页地址,然后读取数据即可
-
* 3,如果nand起始地址不是页对齐,则使用下面的nand_read2函数。
-
* 使用本函数,nand起始地址一定要页对齐
-
* 4,本函数的读操作是以页为单位进行的,size即使不是页的整数倍,也会读取整数倍的页数据。
-
* 5,如果这里的起始地址,非页对齐的地址,可能会导致我的程序重启(应该是读指令异常)
-
*/
-
void nand_read_1(unsigned char *buf, unsigned long start_addr, int size)
-
{
-
unsigned long i = 0, j = 0;
-
//1. 选中,片选
-
NF_CE_L();
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
//清除RB
-
NF_CLEAR_RB();
-
// 2. 发出读命令00h,读NandFlash时的第1个命令
-
//0x00命令发出后,即相当于向NFCMD寄存器中写入了0,nandflash控制器会自动发出各种控制信号,
-
//会自动满足时序图的要求,不再需要人工参与了。
-
//这样,nandflash控制器提供的几个寄存器,就简化了对nandflash的操作。
-
NF_CMD(CMD_READ1);
-
//3. 发出地址,写地址 (分5步发出)
-
write_addr(i);
-
//4. 发出读命令30h,读NandFlash时的第2个命令
-
NF_CMD(CMD_READ2);
-
//注意 : 因为这里没有继续发出页内随意读命令,所以当i=2048,2049的时候,
-
//上面发地址的函数write_addr(i);经过处理,已经跳过第0页了,是要读取第1页的第1,2个字节。
-
-
//5. 判断状态,等待NandFlash不忙
-
NF_WAIT_RB();
-
-
//6. 读数据 : 虽然读写过程可以不从页边界开始,但在这里必须从页边界开始读写至页结束
-
//注意 : uboot的命令 : nand dump 0 //从nand的0地址开始,1次读取1页的数据。
-
for(j=0; j<2048; j++, i++)
-
{
-
//1次从nandflash的8位IO口上读取8位数据。
-
//注意:每次读此寄存器,即执行一次下面语句,就会启动对nandflash的1次读操作。
-
//前一次读操作未完成,是不会启动下一次读操作的。
-
*buf = NF_RDDATA8();
-
buf++;
-
}
-
}
-
//关闭片选
-
NF_CE_H();
-
-
}
-
-
-
/*
-
* buf : 目标地址 = 0x30000000,这是SDRAM的起始地址
-
* start_addr : 源地址 = 4096,运行地址在SDRAM中的代码保存在NAND Flash 4096地址开始处
-
* size : 复制长度 = 600K,对于本实验,这是足够了,没有图片的话,16K足够了,有图片了,得300K
-
*/
-
int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size)
-
{
-
unsigned long i = 0;
-
-
//norflash启动开发板
-
if(isBootFromNorFlash())
-
{
-
while (i < size)
-
{
-
buf[i] = *((char *)start_addr);
-
i++;
-
start_addr++;
-
}
-
}
-
else //nandflash启动开发板
-
{
-
nand_read_1(buf, start_addr, size);
-
}
-
-
return 0;
-
}
-
-
-
/*
-
*功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
-
* 数据方向:(Nand的start_addr->SDRAM的buf)
-
*参数 : buf : SDRAM的缓冲区起始地址:0x30001000
-
* start_addr : 不是页编号,是具体的页内地址,可以不是每一个page页的开始地址,
-
* 即可以是非页边界地址,即地址没有2K对齐的要求。当然地址是页对齐也会支持
-
* size : 复制的字节数,可以是任意字节数。
-
* 这个大小仅仅只包括页内main区的空间,不包括页内spare区的空间。
-
*注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
-
* 2,如果在读取数据的过程中不进行ECC校验判断,则读操作比较简单,
-
* 在发出读命令的两个周期之间,发出要读取的页地址,然后读取数据即可
-
* 3,如果nand起始地址不是页对齐,则使用下面的nand_read2函数。
-
* 当然,页对齐的地址,也可以使用本函数。
-
* 4,本函数已经测试通过,可以使用,功能很强大,完全兼容上面的nand_read函数。
-
*/
-
unsigned int nand_read2(unsigned char * buf, unsigned long start_addr, int size)
-
{
-
unsigned long i = 0, j = 0;
-
unsigned long page_num = 0; //读取的页page的数量
-
unsigned long page_yu_num = 0; //不足一页page的字节数
-
unsigned long start_yu_num = 0; //start_addr起始地址 到 本页末尾地址 的个数。
-
-
page_num = size/2048; //+1=读取的页page的数量
-
page_yu_num = size & (2048 -1); //不足一页page的字节数
-
start_yu_num = 2048-(start_addr & (2048 -1)); //这个非对齐的页的起始地址 到 本页末尾地址 的个数。
-
-
//1. 选中,片选
-
NF_CE_L();
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
//清除RB
-
NF_CLEAR_RB();
-
// 2. 发出读命令00h,读NandFlash时的第1个命令
-
NF_CMD(CMD_READ1);
-
//3. 发出地址,写地址 (分5步发出)
-
write_addr(i);
-
//4. 发出读命令30h,读NandFlash时的第2个命令
-
NF_CMD(CMD_READ2);
-
//5. 判断状态,等待NandFlash不忙
-
NF_WAIT_RB();
-
//6. 读数据
-
if((i & (2048 -1)) == 0) //起始地址:是页对齐的地址
-
{
-
if(page_num > 0) //剩下需要读取的字节数>1页page
-
{
-
for(j=0; j<2048; j++, i++) //读取一页
-
{
-
*buf = NF_RDDATA8();
-
buf++;
-
}
-
page_num--; //每读一页,则少一页
-
}
-
else //剩下需要读取的字节数<1页page
-
{
-
for(j=0; j<page_yu_num; j++, i++) //读取不足一页的数据
-
{
-
*buf = NF_RDDATA8();
-
buf++;
-
}
-
}
-
}
-
else //起始地址:非页对齐的地址
-
{
-
//start_yu_num:是 这个非对齐的页的起始地址 到 本页末尾地址 的个数。
-
for(j=0; j<start_yu_num; j++, i++) //小于2048也行
-
{
-
*buf = NF_RDDATA8();
-
buf++;
-
}
-
}
-
}
-
//7. 取消选中,关闭片选
-
NF_CE_H();
-
-
return 0;
-
}
-
-
-
/*
-
*函数功能 : 页内地址随意读,可以读取页内2047以内的main区地址,也可以读取页内2047以外的64字节的spare区的地址。
-
*函数参数 : page_number : 是页编号,是第几个页。从第9页开始。
-
* addr : 页内地址,这个地址的范围一定是: <=2K+后面的64字节的
-
*注意 : 1, 页读和页写,一般是从页的首地址开始读、写,
-
* 随意读、写实现了在一页范围内任意地址的读、写。
-
* 2, 随意读操作是在页读操作后输入随意读命令和页内列地址,
-
* 这样就可以读取到列地址所指定地址的数据。
-
*页读/写,页内地址随意读/写的区别:
-
* 页读/写 : 虽然读写过程可以不从页边界开始,但在正式场合下还是建议从页边界开始读写至页结束
-
* 页内地址随意读/写 : 可以读取页内2047以内的main区地址,也可以读取页内2047以外的64字节的spare区的地址。
-
*/
-
unsigned char nand_read_random(unsigned long page_number, unsigned long addr)
-
{
-
unsigned char ch;
-
//1,打开Nand Flash片选
-
NF_CE_L();
-
//清RnB信号
-
NF_CLEAR_RB();
-
//2,页读命令周期1
-
NF_CMD(CMD_READ1);
-
//3,写入5个地址周期
-
NF_ADDR(0x00); //列(页内)地址A0~A7
-
NF_ADDR(0x00); //列(页内)地址A8~A11
-
NF_ADDR(page_number & 0xff); //行(页)地址A12~A19
-
NF_ADDR((page_number >> 8) & 0xff); //行(页)地址A20~A27
-
NF_ADDR((page_number >> 16) & 0x01); //行(页)地址A28 (低1位)
-
//4,页读命令周期2
-
NF_CMD(CMD_READ2);
-
//等待RnB信号变高,即不忙
-
NF_WAIT_RB();
-
//5,随意读命令周期1
-
NF_CMD(CMD_RANDOMREAD1);
-
//6,写页内地址,共12根线,可寻址2^12=2K*2空间,每一页的main区是2K空间,spare区只有64字节
-
//如果读取地址是2048+6,没有像write_addr()函数一样,来截取低11位,则A11肯定是1,
-
//后面的+6,就可以寻址到页内spare区的第6个字节。
-
NF_ADDR((char)(addr & 0xff)); //列(页内)地址A0~A7
-
NF_ADDR((char)(addr >> 8) & 0x0f); //列(页内)地址A8~A11 (低4位)
-
//7,随意读命令周期2
-
NF_CMD(CMD_RANDOMREAD2);
-
//8, 读取指定地址的数据
-
ch = NF_RDDATA8();
-
//9, 关闭Nand Flash片选
-
NF_CE_H();
-
-
return ch; //将读取的数据返回
-
}
-
-
-
/*
-
*功能 : 识别坏块的读取: 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
-
* 数据方向:(Nand的start_addr->SDRAM的buf)
-
*参数 : buf : SDRAM的缓冲区起始地址:0x30001000
-
* start_addr : 不是页编号,是 页(2K)对齐的地址。
-
* size : 复制的字节数,是页空间整数倍
-
*注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
-
* 2,读操作是以页为单位进行的
-
* 3,为了更准确地读取数据,则在读取完数据之后还要进行ECC校验判断,以确定所读取的数据是否正确。
-
* 4,为了配合ECC校验,程序的输入参数start_addr最好直接就为K9F2G08U0A的第几页
-
* 例如我们要读取第128064页中的内容,可以调用该程序为 : start_addr=128064*2048
-
* 由于第128064页是第2001块中的第0页 start_addr=(128064=2001×64+0)*2048
-
* 即 start_addr 最好是 页(2K)对齐的地址
-
*/
-
unsigned int nand_read_oob(unsigned char * buf, unsigned long start_addr, int size)
-
{
-
unsigned long ret = 0;
-
unsigned long col, page;
-
unsigned long i = 0;
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
col = start_addr & (2048 - 1); //截取低位 : 如果start_addr是页对齐地址,则col肯定是0
-
page = start_addr / 2048; //截取高位 : 是第几个页
-
-
ret = nand_read_oob_page(buf, page);
-
if(ret == 0) //读取成功
-
{
-
i = i + 2048; //继续写
-
buf = buf + 2048;
-
}
-
else if(ret == 1) //读取失败
-
{
-
//这里应该打印报错语句
-
return 1;
-
}
-
}
-
return 0;
-
}
-
-
-
/*
-
*功能 : 识别坏块的读取: 从NandFlash位置第page_number页开始读取数据,将数据复制到buf处,
-
* 一次共复制一页(2K)个字节的数据,且只复制一页的数据。
-
* 数据方向:(Nand的start_addr--->SDRAM的buf)
-
*参数 : buf : SDRAM的缓冲区起始地址:0x30001000
-
* page_number : 是页编号,是第几个页。
-
*注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到buf。
-
* 2,读操作是以页为单位进行的,并且一次只读一页数据。
-
* 3,为了更准确地读取数据,则在读取完数据之后还要进行ECC校验判断,以确定所读取的数据是否正确。
-
* 4,这段程序是把某一页的内容读取到数组buffer中。该程序的输入参数直接就为K9F2G0
-
* 8U0A的第几页,例如我们要读取第128064页中的内容,可以调用该程序为:nand_read_oob_page(128064)。
-
* 由于第128064页是第2001块中的第0页(128064=2001×64+0),所以为了更清楚地表示页
-
* 与块之间的关系,也可以写为:nand_read_oob_page(2001*64)。
-
*/
-
unsigned int nand_read_oob_page(unsigned char * buf, unsigned long page_number)
-
{
-
unsigned long i;
-
unsigned long meccd; //保存从OOB区读取出来的值,共4个字节:第2048+2049+2050+2051字节
-
unsigned long seccd; //保存从OOB区读取出来的值,共2个字节:第2052+2053字节
-
-
//复位ECC
-
NF_RSTECC();
-
//解锁main区ECC : 以便main区产生ECC码
-
NF_MECC_UnLock();
-
//1. 选中,片选
-
NF_CE_L();
-
//清除RB
-
NF_CLEAR_RB();
-
// 2. 发出读命令00h,读NandFlash时的第1个命令
-
NF_CMD(CMD_READ1);
-
//3. 发出地址,写地址 (分5步发出)
-
NF_ADDR(0x00);
-
NF_ADDR(0x00);
-
NF_ADDR(page_number & 0xff);
-
NF_ADDR((page_number >>8) & 0xff);
-
NF_ADDR((page_number >>16) & 0xff);
-
//4. 发出读命令30h,读NandFlash时的第2个命令
-
NF_CMD(CMD_READ2);
-
//5. 判断状态,等待NandFlash不忙
-
NF_WAIT_RB();
-
//6. 读数据
-
for(i=0; i<2048; i++)
-
{
-
//1次从nandflash的8位IO口上读取8位数据。
-
buf[i] = NF_RDDATA8();
-
}
-
//注意 : 读取完数据之后,硬件将自动产生main区ECC码
-
//锁定main区ECC值 : 自动产生的main区ECC码,不是保存到寄存器(NFMECCD0,1)中,是保存在另外一个位置。
-
//NFMECCD0,1用来存放已有的ECC码,已有的ECC码保存在OOB区,即spare区。
-
NF_MECC_Lock();
-
-
//解锁spare区ECC : 以便spare区产生ECC码
-
NF_SECC_UnLock();
-
//读spare区的前32位(4个字节)地址内容,即第2048~2051地址,这4个字节为写数据时,产生的main区的ECC码。
-
meccd = NF_RDDATA();
-
//meccd的顺序 : ECC0(0~7位)+ECC1(8~15位)+ECC2(16~23位)+ECC3(24~31位)
-
//注意 : 把读取到的main区的ECC校验码放入 NFMECCD0 和 NEMECCD1 的相应位置内。
-
//具体放置位置 : ECC0:NFMECCD0的0~7位 ECC1:NFMECCD0的16~23位。
-
// ECC2:NFMECCD1的0~7位 ECC3:NFMECCD1的16~23位。
-
rNFMECCD0 = ((meccd & 0xff00) << 8) | (meccd & 0xff);
-
rNFMECCD1 = ((meccd & 0xff000000) >>8) | ((meccd & 0xff0000)>>16);
-
//锁定spare区的ECC值 : 自动产生的spare区ECC码,不是保存到NFSECCD中,是保存在另外一个位置。
-
//NFSECCD用来存放已有的spare区的ECC码,已有的spare区ECC码保存在OOB区。
-
NF_SECC_Lock();
-
-
//继续读spare区的4个地址内容,即第2052~2055地址,其中前2个字节为spare区的ECC值
-
seccd = NF_RDDATA();
-
//把读取到的spare区的ECC校验码放入NFSECCD的相应位置内
-
//seccd的顺序 : spare区ECC0(0~7位)+spare区ECC1(8~15位)
-
//具体放置位置 : spare区ECC0:NFSECCD的0~7位。
-
// spare区ECC1:NFSECCD的16~23位。
-
rNFSECCD = ((seccd & 0xff00) << 8) | (seccd & 0xff);
-
//7. 取消选中,关闭片选
-
NF_CE_H();
-
-
//读完一页数据之后,自动算出一个ECC码,这个自动算出来的ECC代码 和
-
//已经存在的ECC码(由OOB区保存到NFMECCD0+NFMECCD1+NFMECCD中),自动进行比较,
-
//比较的结果,保存在NFESTAT寄存器中,[1:0]=00:表示main区无错误 [3:2]=00:表示spare区无错误。
-
if((rNFESTAT0 & 0xf) == 0)
-
{
-
return 0; //读取的数据都正确
-
}
-
else
-
{
-
return 1; //读取完成,但是数据错误
-
}
-
}
-
-
-
/*
-
*功能 : 擦除指定的块
-
*参数 : block_number : 块号
-
*注意 : 1,一个NandFlash块有2K个块。块大小为128K+2K字节。
-
* 2,块被擦除后nand对应的块中的数据全部是1
-
* 3,命令字分为2个阶段
-
* 4,擦除是以块为单位进行的
-
*/
-
unsigned char nand_erase(unsigned long block_number)
-
{
-
unsigned long i;
-
unsigned char stat;
-
//1,选中,片选
-
NF_CE_L();
-
//清除RB
-
NF_CLEAR_RB();
-
//2,发出擦除命令0x60,写入第1个擦除命令
-
NF_CMD(CMD_ERASE1);
-
//写块的地址 : 注意 : 只写块的地址就行,块地址都在行(页)地址的高位上,
-
//所以前面2个周期的列(页内)地址写0就行,后面3个周期的页地址的高11位要具体写出。
-
//列地址 : 就是页内地址, 共12位
-
//行地址 : 就是页地址, 共17位 : 高11位(A28-A18)是2K的块地址,低6位是64的页地址。
-
-
//32位block_number:低11位有效 :
-
//| 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
-
// 对应最后3个地址周期的页内地址的高11位 :
-
//| A28 | A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 | A19 | A18 |
-
//高11位在后面3个地址周期中应是如下的分布 :
-
//| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
-
//| | | | | | | | A28 |
-
//| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
-
//| A19 | A18 | | | | | | |
-
//3,仅需发出块地址(分3步发出)
-
NF_ADDR((block_number << 6) & 0xFF);
-
NF_ADDR((block_number >> 2) & 0xFF);
-
NF_ADDR((block_number >> 10) & 0xFF);
-
//4, 发出擦除命令0xD0, 写入第2个擦除命令
-
NF_CMD(CMD_ERASE2);
-
-
for(i=0; i<1000; i++);
-
//5, 写入读状态寄存器的命令
-
NF_CMD(CMD_STATUS);
-
//6, 判断状态
-
do
-
{
-
//读取8位状态数据
-
stat = NF_RDDATA8();
-
}
-
while(!(stat & 0x40)); //等到第6位为1 : 表示读取数据完成
-
//7, 取消选中,关闭片选
-
NF_CE_H();
-
-
//约定用0x66来表示擦除成功,或者写成功
-
return 0;
-
}
-
-
-
/*
-
*函数功能 : 识别坏块的擦除
-
*函数参数 : 块编号,从第0块开始,2K个块(第0块~第2047块)
-
*函数返回值 :
-
* 1 : 是坏块,不能擦除
-
*注意 : 1,擦除是以块为单位进行的,因此在写地址周期时,只需写三个行周期,并且只写块地址
-
* 并且,擦除的范围是64个页,页内的main区+spare区的64字节都擦除成0xff。
-
* 2,只能擦除好块,不能擦除坏块,擦除坏块的操作会失败。
-
* 也可以根据这个特性来判断一个块是不是坏块:当第1页的spare的第6个字节的值感觉不可靠,
-
* 并且,这个块的数据不重要,可以清除时,可以直接擦除这个块,如果擦除成功,表示是好块,
-
* 如果擦除失败,则表示这个块是坏块,我们再在spare区的第6个字节标记一下,这是个坏块。
-
* 3,该程序的输入参数为K9F2G08U0A的第几块,例如我们要擦除第2001块,
-
* 则调用该函数为 : nand_erase_oob(2001)。
-
-
//写地址(要读取数据的起始地址) :
-
//包括 : 列(页内)地址,是低位地址,共A0~A11,在具体的page页内寻址。2^12=2K*2 一个page页=(2K+64)字节
-
// 行(页)地址,是高位地址,共A12~A28,可以寻址到page页。2^17=128K, 一个Nand器件=2K*64个page页 = 128K页
-
// 高位地址中,A12~A17,可以寻址到block块下面的page页。2^6=64, 一个block块=64个page页
-
// 高位地址中,A18~A28,可以寻址到block块。2^11=2K, 一个Nand器件=2K个block块
-
//要写全部的5个周期的地址
-
-
//32位的page_number : 低29位有效 :
-
//| 28| 27| 26| 25| 24| 23| 22| 21| 20| 19| 18| 17| 16| 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
-
// 对应最后3个地址周期的页内地址的高11位 :
-
//|A28|A27|A26|A25|A24|A23|A22|A21|A20|A19|A18|A17|A16|A15|A14|A13|A12|A11|A10|A09|A08|A07|A06|A05|A04|A03|A02|A01|A00|
-
//29位在5个地址周期中应是如下的分布 :
-
//| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
-
//| L | L | L | L | L | L | L | A28 |
-
//| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
-
//| A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 |
-
//| L | L | L | L | A11 | A10 | A9 | A8 |
-
//| A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
-
*/
-
unsigned int nand_erase_oob(unsigned long block_number)
-
{
-
unsigned long i = 0;
-
unsigned char stat; //保存 状态寄存器 中的值
-
unsigned int flag; //坏块标志
-
//判断该块是否为坏块
-
flag = nand_IsBadBlock(block_number);
-
if(flag == 1) //是坏块
-
{
-
return 1; //是坏块,返回1
-
}
-
//本块不是坏块,才继续走到下面来
-
//1,选中,片选
-
NF_CE_L();
-
//清除RB
-
NF_CLEAR_RB();
-
//2,发出擦除命令0x60,写入第1个擦除命令
-
NF_CMD(CMD_ERASE1);
-
//3,仅需发出块地址(分3步发出)
-
//行(页)地址,是高位地址,共A12~A28,可以寻址到page页。
-
//A12~A17,可以寻址到block块下面的page页; A18~A28,可以寻址到block块。
-
NF_ADDR((block_number << 6) & 0xc0); //行(页)地址 A18~A19(高2位)
-
NF_ADDR((block_number >> 2) & 0xff); //行(页)地址 A20~A27(8位)
-
NF_ADDR((block_number >> 10) & 0x01); //行(页)地址 A28 (低1位)
-
//4, 发出擦除命令0xD0,写入第2个擦除命令
-
NF_CMD(CMD_ERASE2);
-
//会擦除本块的所有64个页,包括页内的main区空间+页内的spare区空间
-
for(i=0; i<100; i++); //延时一段时间
-
//5, 读状态命令
-
NF_CMD(CMD_STATUS);
-
//6, 判断状态
-
do
-
{
-
//将状态寄存器的值 赋值给stat。只取8位状态数据。
-
stat=NF_RDDATA();
-
}
-
while(!(stat & 0x40)); //等到第6位为1 : 表示读取状态寄存器完成。
-
//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
-
//7, 取消选中,关闭片选
-
NF_CE_H();
-
//判断状态寄存器的值的第0位是否为0,为0则表示擦除操作成功完成,否则表示擦除操作失败
-
if(stat & 0x01)
-
{
-
//如果擦除失败,则标记该块为坏块,也即恢复刚才可能擦除的坏块标志。
-
flag = nand_MarkBadBlock(block_number); //标注该块为坏块
-
if(flag == 0)
-
{
-
return 2; //标注坏块成功,擦除操作失败
-
}
-
else
-
{
-
return 3; //标注坏块失败,擦除操作失败
-
}
-
}
-
else
-
{
-
return 0; //擦除操作成功
-
}
-
}
-
-
-
/*功能 : 将buf处开始取数据,写入NandFlash的start_addr地址处,共写入size个字节。
-
* 数据方向:(SDRAM的buf->Nand的start_addr)
-
*参数 : buf : SDRAM的缓冲区起始地址
-
* start_addr : 如果要确保正确使用本函数来传输数据,这里的地址:不是页编号,
-
* 但是必须每一个page页的开始地址,即页边界地址,即地址要2K对齐。
-
* size : 复制的字节数,最好是页空间整数倍。即使不指定为页的整数倍,也会读取2K整数倍的空间
-
*注意 : 1,write是相对于cpu说的,cpu写数据到nand中,SDRAM中的数据拷贝到Nand中。
-
* 2,页写操作的大致流程为:在两个写命令周期之间分别写入页地址和数据
-
* 3,如果为了保证下次读取该数据时的正确性,还需要把main区的ECC值和spare区
-
* 的ECC值写入到该页的spare区内。
-
*/
-
unsigned int nand_write(unsigned char * buf, unsigned long start_addr, int size)
-
{
-
unsigned long i,j;
-
unsigned char stat;
-
//1,选中,片选
-
NF_CE_L();
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
//清除RB
-
NF_CLEAR_RB();
-
//2. 发出写命令0x80,写NandFlash时的第1个命令 : NandFlash写准备
-
NF_CMD(CMD_WRITE1);
-
//3. 发出地址,写地址(分5步发出)
-
write_addr(i);
-
//4, 写数据 : 虽然读写过程可以不从页边界开始,但在这里必须从页边界开始读写至页结束
-
for(j=0; j<2048; j++, i++)
-
{
-
//1次写8位的数据给NandFlash的8位IO口
-
//注意:每次写这个寄存器,即执行一次下面的语句,都会启动一次nandflash的写数据操作,
-
//前一次写数据操作不完成,是不会启动下一次写数据操作的
-
NF_WRDATA8(*buf);
-
buf++;
-
}
-
-
//5. 发出写命令0x10,写NandFlash时的第2个命令 : 启动写操作
-
//此时,Flash内部会自动完成写,校验操作。
-
NF_CMD(CMD_WRITE2);
-
for(j=0; j<100; j++);
-
//6. 判断状态,写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
-
NF_CMD(CMD_STATUS);
-
do
-
{
-
//读取8位状态数据
-
stat = NF_RDDATA8();
-
}
-
while(!(stat & 0x40)); //等到第6位为1 : 表示写数据完成。
-
}
-
//7. 取消选中,关闭片选
-
NF_CE_H();
-
-
return 0;
-
}
-
-
-
/*函数功能 : 将buf处开始取数据,写入NandFlash的start_addr地址处,共写入size个字节。
-
* 数据方向:(SDRAM的buf->Nand的start_addr)
-
*函数参数 : buf : SDRAM的缓冲区起始地址
-
* start_addr : 不是页编号,是具体的页内地址,可以不是每一个page页的开始地址,
-
* 即可以是非页边界地址,即地址没有2K对齐的要求。当然地址是页对齐也会支持
-
* size : 复制的字节数,可以是任意字节数。
-
* 这个大小仅仅只包括页内main区的空间,不包括页内spare区的空间。*注意 : 1,write是相对于cpu说的,cpu写数据到nand中,SDRAM中的数据拷贝到Nand中。
-
*注意 : 1,页写操作的大致流程为:在两个写命令周期之间分别写入页地址和数据
-
* 2,如果为了保证下次读取该数据时的正确性,还需要把main区的ECC值和spare区
-
* 的ECC值写入到该页的spare区内。
-
* 3,如果nand起始地址不是页对齐,则使用下面的 nand_write2 函数。
-
* 当然,页对齐的地址,也可以使用本函数。
-
* 4,本函数已经测试通过,可以使用,功能很强大,完全兼容上面的 nand_write 函数。
-
*/
-
unsigned int nand_write2(unsigned char * buf, unsigned long start_addr, int size)
-
{
-
unsigned long i,j;
-
unsigned char stat;
-
-
unsigned long page_num = 0; //写入的页page的数量
-
unsigned long page_yu_num = 0; //不足一页page的字节数
-
unsigned long start_yu_num = 0; //start_addr起始地址 到 本页末尾地址 的个数。
-
-
page_num = size/2048; //写入的页page的数量
-
page_yu_num = size & (2048 -1); //不足一页page的字节数
-
start_yu_num = 2048-(start_addr & (2048 -1)); //这个非对齐的页的起始地址 到 本页末尾地址 的个数。
-
-
//1,选中,片选
-
NF_CE_L();
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
//清除RB
-
NF_CLEAR_RB();
-
//2. 发出写命令0x80,写NandFlash时的第1个命令 : NandFlash写准备
-
NF_CMD(CMD_WRITE1);
-
//3. 发出地址,写地址(分5步发出)
-
write_addr(i);
-
//4, 写数据 : 虽然读写过程可以不从页边界开始,但在这里必须从页边界开始读写至页结束
-
if((i & (2048 -1)) == 0) //起始地址:是页对齐的地址
-
{
-
if(page_num > 0) //剩下需要写入的字节数>1页page
-
{
-
for(j=0; j<2048; j++, i++)
-
{
-
//1次写8位的数据给NandFlash的8位IO口
-
//注意:每次写这个寄存器,即执行一次下面的语句,都会启动一次nandflash的写数据操作,
-
//前一次写数据操作不完成,是不会启动下一次写数据操作的
-
NF_WRDATA8(*buf);
-
buf++;
-
}
-
}
-
else //剩下需要写入的字节数<1页page
-
{
-
for(j=0; j<page_yu_num; j++, i++) //写入不足一页的数据
-
{
-
NF_WRDATA8(*buf);
-
buf++;
-
}
-
}
-
}
-
else //起始地址:非页对齐的地址
-
{
-
//start_yu_num:是 这个非对齐的页的起始地址 到 本页末尾地址 的个数。
-
for(j=0; j<start_yu_num; j++, i++) //小于2048也行
-
{
-
NF_WRDATA8(*buf);
-
buf++;
-
}
-
}
-
-
//5. 发出写命令0x10,写NandFlash时的第2个命令 : 启动写操作
-
//此时,Flash内部会自动完成写,校验操作。
-
NF_CMD(CMD_WRITE2);
-
for(j=0; j<100; j++);
-
//6. 判断状态,写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
-
NF_CMD(CMD_STATUS);
-
do
-
{
-
//读取8位状态数据
-
stat = NF_RDDATA8();
-
}
-
while(!(stat & 0x40)); //等到第6位为1 : 表示写数据完成。
-
}
-
//7. 取消选中,关闭片选
-
NF_CE_H();
-
-
return 0;
-
}
-
-
-
/*
-
*函数功能 : 页内地址随意写
-
*函数参数 : page_number : 是页编号,从第0页开始。
-
* 1块=64页,第2块最开始的一页是第64页
-
* addr : 页内地址(包括2K的main区空间+64字节的spare区空间)
-
* data : 要写入的1字节的数据
-
*函数返回值 :
-
* 0 : 写入成功
-
* 1 : 写入失败
-
*注意 : 1, 页读和页写是从页的首地址开始读、写,
-
* 随意读、写实现了在一页范围内任意地址的读、写。
-
* 2, 随意写操作是在页写操作的第二个页写命令周期前,输入随意写命令和页内列地址,
-
* 以及要写入的数据,这样就可以把数据写入到列地址所指定的地址内。
-
*/
-
unsigned int nand_write_random(unsigned long page_number, unsigned long addr, unsigned char data)
-
{
-
unsigned int i = 0;
-
unsigned char stat; //保存状态寄存器的值
-
//1,打开Nand Flash片选
-
NF_CE_L();
-
//清RnB信号
-
NF_CLEAR_RB();
-
//2,页写命令周期1
-
NF_CMD(CMD_WRITE1);
-
//3,写入5个地址周期
-
NF_ADDR(0x00); //列地址A0~A7
-
NF_ADDR(0x00); //列地址A8~A11
-
NF_ADDR(page_number & 0xff); //行地址A12~A19
-
NF_ADDR((page_number >> 8) & 0xff); //行地址A20~A27
-
NF_ADDR((page_number >> 16) & 0x01); //行地址A28
-
//4,随意写命令
-
NF_CMD(CMD_RANDOMWRITE);
-
//5,页内地址
-
NF_ADDR((char)(addr & 0xff)); //列地址A0~A7
-
NF_ADDR((char)((addr >> 8) & 0x0f)); //列地址A8~A11
-
//6,写入数据
-
NF_WRDATA8(data);
-
//7,页写命令周期2
-
NF_CMD(CMD_WRITE2);
-
for(i=0; i<100; i++); //延时一段时间
-
//8. 判断状态,写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
-
NF_CMD(CMD_STATUS);
-
do
-
{
-
stat = NF_RDDATA8();
-
}
-
while(!(stat&0x40)); //等待第6位为1,表示写数据完成
-
//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
-
-
//9,关闭Nand Flash片选
-
NF_CE_H();
-
-
//判断状态值的第0位是否为0,为0则写操作正确,否则错误
-
if(stat &0x1)
-
{
-
return 1; //写入失败
-
}
-
else
-
{
-
return 0; //写入成功
-
}
-
}
-
-
-
/*功能 : 识别坏块的写 : 将buf处开始的数据,写入NandFlash的start_addr地址处,共写入size个字节。
-
* 数据方向:(SDRAM的buf->Nand的start_addr)
-
*参数 : buf : SDRAM的缓冲区起始地址
-
* start_addr : 不是页编号,是页对齐地址,
-
* 是具体的读取NandFlash的起始地址,包括页内地址
-
* size : 复制的字节数,是页空间整数倍,也就是说,size是2K整数倍
-
*注意 : 1,write是相对于cpu说的,cpu写数据到nand中,SDRAM中的数据拷贝到Nand中。
-
* 2,坏块的话写进去的东西肯定是不对的。一般情况下如果你手上的系统支持NAND运行,
-
* 那么相关的读取数据代码中肯定有跳坏块处理的
-
*/
-
unsigned int nand_write_oob(unsigned char * buf, unsigned long start_addr, int size)
-
{
-
unsigned long ret = 0;
-
unsigned long col, page;
-
unsigned long i = 0;
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
col = start_addr & (2048 - 1); //如果start_addr是页对齐地址,则col肯定是0
-
page = start_addr / 2048; //截取高位,是第几个页
-
-
ret = nand_write_oob_page(buf, page);
-
if(ret == 0) //写成功
-
{
-
i = i + 2048; //跳过坏块
-
buf = buf + 2048; //继续写
-
}
-
else if(ret == 1) //是坏块
-
{
-
i = i + 2048; //跳过坏块
-
buf = buf; //再写,buf不增加
-
}
-
else if(ret == 2) //写失败,但是标注坏块成功,判断是坏块
-
{
-
//这里应该打印一句报错信息
-
i = i + 2048; //跳过坏块
-
buf = buf; //再写,buf不增加
-
}
-
else if(ret == 3) //写失败,标注坏块也失败,读取的时候,无法判断是否是坏块,这里当做好块
-
{
-
//这里应该打印一句报错信息
-
i = i; //当做好块,再写一次
-
buf = buf; //再写,buf不增加
-
}
-
}
-
return 0;
-
}
-
-
-
/*功能 : 识别坏块的写 : 将数据 从SDRAM的buf 复制到 NandFlash第page_number页开始的位置。
-
* 一次写且仅写一页(2K)个字节的数据
-
* 数据方向:(SDRAM的buf--->nandflash的第page_number页)
-
*参数 : buf : SDRAM的缓冲区起始地址
-
* page_number : 是页编号,是第几页,从第0页开始
-
*注意 : 1,write是相对于cpu说的,cpu写数据到nand中, buf中的数据拷贝到Nand中。
-
* 2,写操作是以页为单位进行的,并且一次只写一页数据
-
* 3,本程序检查坏块,标记坏块,并且将ECC码记录在OOB区。
-
*/
-
unsigned int nand_write_oob_page(unsigned char * buf, unsigned long page_number)
-
{
-
unsigned long i;
-
unsigned long mecc0; //保存 NFMECC0 寄存器的值,32位值均有效,即4个字节:ECC0+ECC1+ECC2+ECC3
-
unsigned long secc; //保存 NFSECC 寄存器的值,低16位值有效,即2个字节:spare区ECC0+spare区ECC1
-
unsigned char stat;
-
unsigned int flag; //坏块标志,标注成功与否的标志
-
unsigned char ECCBuf[6]; //用来保存 上面的6个字节:mecc0+secc
-
-
//判断该块是否为坏块
-
flag = nand_IsBadBlock(page_number>>6);
-
if(1 == flag)
-
{
-
return 1; //是坏块,返回
-
}
-
//复位ECC
-
NF_RSTECC();
-
//解锁main区的ECC : 以便产生该区的ECC码
-
NF_MECC_UnLock();
-
//1,选中,片选
-
NF_CE_L();
-
//清除RB
-
NF_CLEAR_RB();
-
//2. 发出写命令0x80,写NandFlash时的第1个命令 : NandFlash写准备
-
NF_CMD(CMD_WRITE1);
-
//3. 发出地址,写地址(分5步发出)
-
NF_ADDR(0x00);
-
NF_ADDR(0x00);
-
NF_ADDR(page_number & 0xff);
-
NF_ADDR((page_number >>8) & 0xff);
-
NF_ADDR((page_number >>16) & 0xff);
-
//4, 写数据 : 一般1次写1页的数据
-
for(i=0; i<2048; i++)
-
{
-
//1次写8位的数据给NandFlash的8位IO口
-
//注意:每次写这个寄存器,即执行一次下面的语句,都会启动一次nandflash的写数据操作,
-
//前一次写数据操作不完成,是不会启动下一次写数据操作的
-
NF_WRDATA8(buf[i]);
-
//buf++;
-
}
-
//注意 : main区写完数据之后,硬件将自动产生main区的ECC码,共4个字节。
-
//锁定main区的ECC值 : 会自动把产生的main区的ECC码,保存到相应的寄存器(NFMECC0)中。
-
NF_MECC_Lock();
-
//读取main区的ECC校验码,保存到本地变量mecc0中。
-
mecc0=rNFMECC0;
-
//把ECC校验码由字型转换为字节型,并保存到字节变量数组ECCBuf中
-
ECCBuf[0] = (unsigned char)(mecc0 & 0xff); //0~7位
-
ECCBuf[1] = (unsigned char)((mecc0 >> 8) & 0xff); //8~15位
-
ECCBuf[2] = (unsigned char)((mecc0 >> 16) & 0xff); //16~23位
-
ECCBuf[3] = (unsigned char)((mecc0 >> 24) & 0xff); //24~31位
-
-
//解锁spare区的ECC :
-
NF_SECC_UnLock();
-
//把main区的ECC值写入到spare区的前4个字节地址内,即第2048~2051地址
-
for(i=0; i<4; i++)
-
{
-
//注意 : 页内main区写满之后,继续写,会写到本页的第2049个地址,即开始写spare区的第1个字节。
-
NF_WRDATA8(ECCBuf[i]);
-
}
-
//注意 : spare区写完数据之后,硬件将自动产生spare区的ECC码,共2个字节。
-
//锁定spare区的ECC值 : 会自动把产生的spare区的ECC码,保存到相应的寄存器(NFSECC)中。
-
NF_SECC_Lock();
-
-
//读取spare区的ECC校验码,保存到本地变量secc中。
-
secc = rNFSECC;
-
//把ECC校验码保存到字节变量数组ECCBuf中
-
ECCBuf[4]=(unsigned char)(secc & 0xff); //0~7位
-
ECCBuf[5]=(unsigned char)((secc >> 8) & 0xff); //8~15位
-
//把spare区的ECC值继续写入到spare区,第2052~2053地址内。
-
//注意 : 每块的第0页的spare区,第2054个字节处,会有坏块标志,其他页没有。
-
for(i=4; i<6; i++)
-
{
-
NF_WRDATA8(ECCBuf[i]);
-
}
-
//5. 发出读命令0x10,写NandFlash时的第2个命令 : 启动写操作
-
NF_CMD(CMD_WRITE2);
-
for(i=0; i<100; i++); //延时一段时间,以等待写操作完成
-
//6. 判断状态,写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
-
NF_CMD(CMD_STATUS);
-
do
-
{
-
//读取8位状态数据
-
stat = NF_RDDATA8();
-
}
-
while(!(stat & 0x40)); //等到第6位为1 : 表示写数据完成。
-
//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
-
-
//7. 取消选中,关闭片选
-
NF_CE_H();
-
//判断状态值的第0位是否为0,为0则写操作正确,否则错误
-
if(stat & 0x1)
-
{
-
//如果写入失败,则标记坏块。
-
//A12~A17,可以寻址到block块下面的page页; A18~A28,可以寻址到block块。
-
//右移6位,表示去掉 A12~A17,只剩下 块地址 A18~A28 共11位的有效值。
-
flag = nand_MarkBadBlock(page_number>>6);
-
if(0 == flag)
-
{
-
return 2; //标注坏块成功,写入操作失败
-
}
-
else
-
{
-
return 3; //标注坏块失败,写入操作失败
-
}
-
}
-
else
-
{
-
return 0; //写操作成功
-
}
-
}
-
-
-
/*
-
*函数功能 : 检查指定的块是否是坏块
-
*函数参数 : 块的编号,从第0块开始
-
*函数返回值 : 0 : 好块 1 : 坏块
-
*注意 :
-
* 1, 为了和固有坏块信息保持一致,将新发现的坏块的第1个page的 spare area的第6个Byte标记为非0xff的值。
-
* 我们定义在spare区的第6个地址(即每页的第2054地址)用来标注坏块,0x44表示该块为坏块
-
* 要判断坏块时,利用随意读命令来读取2054地址的内容是否为0x44,正常是0xff
-
* 2, 坏块的特性是:当编程/擦除这个块时,不能将某些位拉高,这 会造成Page Program和
-
* Block Erase操作时的错误,相应地反映到Status Register的相应位。
-
* 我们去查看 Status Register 的相应位,我们就可以找到这个坏块是怎么来的了。即可知是什么原因造成的坏块。
-
* 3, NAND Flash出厂时在OOB中已经反映出了坏块信息,因此,如果在擦除一个块之前,
-
* 一定要先check一下OOB的第6个byte是否是0xff,如果是就证明这是一个好块,
-
* 可以擦除;如果是非0xff,那么就不能擦除。
-
* 当然,这样处理可能会犯一个错误―――“错杀伪坏块”,因为在芯片操作过程中可能
-
* 由于电压不稳定等偶然因素会造成NAND操作的错误。但是,为了数据的可 靠性及软件设计
-
* 的简单化,我们就要奉行“蒋委员长”的“宁可错杀一千,也决不放过一个”的宗旨。
-
* 4, 如果在对一个块的某个page进行编程的时候发生了错误就要把这个块标记为坏块,
-
* 首先就要把其他好的page里面的内容备份到另外一个空的好块里面,然后,把这个块标记为
-
* 坏块。
-
* 5, 当然,这可能会犯“错杀”之误,一个补救的办法,就是在进行完页备份之后,
-
* 再将这个块擦除一遍,如果Block Erase发生错误,那就证明这个块是个真正的坏块,那就毫不犹豫地将它打个“戳”吧!
-
* 6, 1个块有128K,64个页,为什么是第1页的OOB区的第6个Byte?这个是各个厂家默认的规定。
-
* 即这是NAND Flash生产商的默认约定,你可以看到Samsung,Toshiba,STMicroelectronics都是使用这个Byte作为坏块标记的。
-
*/
-
unsigned int nand_IsBadBlock(unsigned long block)
-
{
-
unsigned char ch;
-
-
//如果是第0块,0*64=0,则第1个参数是第0页
-
//如果是第1块,1*64=64,则第1个参数是第64页。
-
//第0~63页是第0块,第64页是第1块最开始的一页
-
ch = nand_read_random(block*64, 2048+6);
-
if(0xFF == ch) //是好块
-
{
-
return 0;
-
}
-
else //是坏块
-
{
-
return 1;
-
}
-
}
-
-
-
/*
-
*函数功能 : 标注坏块,标注该页所在的块为坏块
-
*函数参数 : 块的编号,从第0块开始
-
*函数返回值 :
-
* 0 : 标注坏块 成功
-
* 1 : 标注坏块 失败
-
*注意 : 1,要标注坏块时,利用随意写命令来向2048+6地址写0x33
-
* 2, 输入参数都为块地址,也就是即使仅仅一页出现问题,我们也标注整个块为坏块。
-
* 3,标注位置 : 约定是在 本块,第0页的第2054个字节处。即本块第0页的spare区的第6个字节处。其他页没有。
-
*/
-
unsigned int nand_MarkBadBlock(unsigned long block)
-
{
-
unsigned char flag;
-
-
//如果是第0块,0*64=0,则第1个参数是第0页
-
//如果是第1块,1*64=64,则第1个参数是第64页。
-
//第0~63页是第0块,第64页是第1块最开始的一页
-
flag = nand_write_random(block*64, 2048+6, 0x33);
-
if(0 == flag) //写入成功
-
{
-
return 0; //标注坏块 成功
-
}
-
else
-
{
-
return 1; //标注坏块 失败
-
}
-
}
-
-
-
/*
-
坏块管理:
-
1,我们以前没有进行坏块管理,为什么也能直接使用nandflash?
-
因为三星公司为了支持nandflash的启动,确保了第0块没有坏块,所以我们读/写/擦除第0块是不会出现问题的。
-
2,第0块之后的块,是可能出现坏块的。
-
3,本程序没有实现坏块表BBT,有时间要实现一下。
-
*/
-
-
/*
-
测试
-
-
*/
nand.h文件
-
#ifndef _NAND_H_
-
#define _NAND_H_
-
-
/*NANDFLASH配置寄存器 : NFCONF:用来设置时序参数宽度,数据位宽*/
-
#define rNFCONF (*(volatile unsigned long *)0x4E000000)
-
/*NANDFLASH控制寄存器 : NFCONT:用来禁止/使能nandflash控制器,使能/禁止片选信号*/
-
#define rNFCONT (*(volatile unsigned long *)0x4E000004)
-
/*NANDFLASH命令集寄存器 : NFCMMD:用来向nandflash发出命令信号*/
-
#define rNFCMMD (*(volatile unsigned long *)0x4E000008)
-
/*NANDFLASH地址集寄存器 : NFADDR:用来向nandflash发出地址信号*/
-
#define rNFADDR (*(volatile unsigned long *)0x4E00000C)
-
/*NANDFLASH数据寄存器 : NFDATA,32位=0x4E000010+0x4E000011+0x4E000012+0x4E000013*/
-
#define rNFDATA (*(volatile unsigned long *)0x4E000010)
-
/*截取NANDFLASH数据寄存器的8位 : 仅0x4E000010,实际上每次读写也只用到低8位,不可能用到32位。
-
每次读写此寄存器,就将启动一次对nandflash的读数据写数据的操作。*/
-
#define rNFDATA8 (*(volatile unsigned char *)0x4E000010)
-
/*NANDFLASH运行状态寄存器 : NFSTAT:只用了最低位,0:busy,1:ready*/
-
#define rNFSTAT (*(volatile unsigned long *)0x4E000020)
-
-
-
-
-
-
/************************************************************************/
-
//下面3个寄存器 : 是在读取nandflash数据的时候,需要将已有的ECC写入下面3个寄存器,已有的ECC保存在OOB区中。
-
//读取完毕之后,会自动算出一个ECC,再将这个已有的ECC和这个刚算出来的ECC比较。比较的结果会自动放在NFESTAT寄存器中。
-
/*main区ECC寄存器0,本nandflash只有8位IO口
-
只有16位数据有效,但不是连续的16位 : 0~7位:ECC0; 16~23位:ECC1*/
-
#define rNFMECCD0 (*(volatile unsigned long *)0x4e000014)
-
/*main区ECC寄存器1,本nandflash只有8位IO口
-
只有16位数据有效,但不是连续的16位 : 0~7位:ECC2; 16~23位:ECC3*/
-
#define rNFMECCD1 (*(volatile unsigned long *)0x4e000018)
-
/*spare区ECC寄存器,本nandflash只有8位IO口
-
只有16位数据有效,但不是连续的16位 : 0~7位:spare区ECC0; 16~23位:spare区ECC1*/
-
#define rNFSECCD (*(volatile unsigned long *)0x4e00001c)
-
-
/*nandflash的ECC状态寄存器0*/
-
#define rNFESTAT0 (*(volatile unsigned long *)0x4e000024)
-
/*nandflash的ECC状态寄存器1,本nandflash只有8位IO口,所以只用上面的NFESTAT0寄存器就行了,NFESTAT1是适用于nandflash有16位IO口的。*/
-
#define rNFESTAT1 (*(volatile unsigned long *)0x4e000028)
-
-
//下面3个寄存器 : 是在将数据写入nandflash的时候,硬件自动生成的ECC码,硬件自动保存的位置,我们一般再保存到OOB区中。
-
/*main区ECC寄存器0,只适用于nandflash的低8位IO口
-
32位数据均有效 : 0~7位:ECC0; 8~15位:ECC1; 16~23位:ECC2; 24~31位:ECC3*/
-
#define rNFMECC0 (*(volatile unsigned long *)0x4e00002c)
-
/*main区ECC寄存器1,本nandflash只有8位IO口,所以只用上面的NFMECC0寄存器就行了,NFMECC1是适用于nandflash有16位IO口的。*/
-
#define rNFMECC1 (*(volatile unsigned long *)0x4e000030)
-
/*spare区ECC寄存器,本nandflash只有8位IO口,只用本寄存器的低2个字节,高2个字节是给16位IO的nandflash用的。
-
低16位数据有效 : 0~7位:spare区ECC0; 8~15位:spare区ECC1*/
-
#define rNFSECC (*(volatile unsigned long *)0x4e000034)
-
/************************************************************************/
-
-
-
-
-
-
/*相关命令如下 : */
-
/*读NandFlash页时的命令1*/
-
#define CMD_READ1 0x00
-
/*读NandFlash页时的命令2*/
-
#define CMD_READ2 0x30
-
-
/*页编程 : 写NandFlash页时的命令1*/
-
#define CMD_WRITE1 0x80
-
/*页编程 : 写NandFlash页时的命令2*/
-
#define CMD_WRITE2 0x10
-
-
/*擦除NandFlash块的命令1*/
-
#define CMD_ERASE1 0x60
-
/*擦除NandFlash块的命令2*/
-
#define CMD_ERASE2 0xD0
-
-
/*读状态寄存器的命令*/
-
#define CMD_STATUS 0x70
-
/*读取芯片ID的命令*/
-
#define CMD_READID 0x90
-
/*重启命令*/
-
#define CMD_RESET 0xFF
-
-
-
-
-
/*随意读命令周期1*/
-
#define CMD_RANDOMREAD1 0x05
-
/*随意读命令周期2*/
-
#define CMD_RANDOMREAD2 0xE0
-
/*随意写命令*/
-
#define CMD_RANDOMWRITE 0x85
-
-
-
-
-
#define NF_CE_L() {(rNFCONT) &= (~(1<<1));}
-
/*禁止nandfalsh : 控制寄存器NFCONT[1]=1*/
-
#define NF_CE_H() {(rNFCONT) |= (1<<1);}
-
-
/*向存储器发出命令 : 命令寄存器NFCMMD[7:0]*/
-
#define NF_CMD(data) {(rNFCMMD) = (data);}
-
/*向存储器发出地址值 : 地址寄存器NFADDR[7:0]*/
-
#define NF_ADDR(data) {(rNFADDR) = (data);}
-
-
/*等待NandFlash准备就绪(即不忙) : 一直等到状态寄存器NFSTAT[0]=1,循环才退出*/
-
#define NF_WAIT_RB() {while(!((rNFSTAT) & (1<<0)));}
-
/*清除RB:即检测RB传输*/
-
#define NF_CLEAR_RB() {(rNFSTAT) |= (1<<2);}
-
/*等待检测RnB : 一直等到状态寄存器NFSTAT[2]=1,循环才退出*/
-
#define NF_DETECT_RB {while(!((rNDSTA) & (1<<2)));}
-
-
/*从NANDFLASH的IO口中读数据(32位) : 数据寄存器NFDATA*/
-
#define NF_RDDATA() (rNFDATA)
-
/*从NANDFLASH的IO口中读数据(8位) : 数据寄存器NFDATA*/
-
#define NF_RDDATA8() (rNFDATA8)
-
/*向NANDFLASH的IO口中写数据(32位) : 数据寄存器NFDATA*/
-
#define NF_WRDATA(data) {(rNFDATA) = (data);}
-
/*向NANDFLASH的IO口中写数据(8位) : 数据寄存器NFDATA*/
-
#define NF_WRDATA8(data) {(rNFDATA8) = (data);}
-
-
-
-
-
/************************************************************************/
-
/*复位ECC:初始化ECC编码器/译码器*/
-
#define NF_RSTECC() {rNFCONT |= (1<<4);}
-
/*解锁main区ECC*/
-
#define NF_MECC_UnLock() {rNFCONT &= (~(1<<5));}
-
/*锁定main区ECC*/
-
#define NF_MECC_Lock() {rNFCONT |= (1<<5);}
-
/*解锁spare区(OOB区)ECC*/
-
#define NF_SECC_UnLock() {rNFCONT &= (~(1<<6));}
-
/*锁定spare区(OOB区)ECC*/
-
#define NF_SECC_Lock() {rNFCONT |= (1<<6);}
-
/************************************************************************/
-
-
-
-
-
/*时序信号参数的宽度*/
-
#define TACLS 1
-
#define TWRPH0 3
-
#define TWRPH1 0
-
-
int isBootFromNorFlash(void);
-
void nand_reset();
-
void nand_init();
-
unsigned char nand_readId();
-
void write_addr(unsigned long addr);
-
void nand_read(unsigned char *buf, unsigned long start_addr, int size);
-
void nand_read_1(unsigned char *buf, unsigned long start_addr, int size);
-
int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size);
-
-
unsigned int nand_read2(unsigned char * buf, unsigned long start_addr, int size);
-
unsigned char nand_read_random(unsigned long page_number, unsigned long addr);
-
unsigned int nand_read_oob(unsigned char *buf, unsigned long start_addr, int size);
-
unsigned int nand_read_oob_page(unsigned char * buf, unsigned long page_number);
-
-
unsigned char nand_erase(unsigned long block_number);
-
unsigned int nand_erase_oob(unsigned long block_number);
-
-
unsigned int nand_write(unsigned char *buf, unsigned long start_addr, int size);
-
unsigned int nand_write_random(unsigned long page_number, unsigned long addr, unsigned char data);
-
unsigned int nand_write_oob(unsigned char *buf, unsigned long start_addr, int size);
-
unsigned int nand_write_oob_page(unsigned char * buf, unsigned long page_number);
-
-
unsigned int nand_IsBadBlock(unsigned long block);
-
unsigned int nand_MarkBadBlock(unsigned long block);
-
-
#endif
spi.lds文件
-
SECTIONS { /*.:表示当前*/
-
. = 0x30000000; /*汇编语言中:.text段中_start的值=0x30000000*/
-
.text : { *(.text) }
-
-
. = ALIGN(4);
-
.rodata : {*(.rodata*)}
-
-
. = ALIGN(4);
-
.data : { *(.data) }
-
-
. = ALIGN(4);
-
__bss_start = .; /*编译出来的2进制程序比如uboot.bin中不会含有static变量初始化为0的存放的空间,这样二进制文件就小了。我们把static变量统一放在bss段,运行程序前,将bss段清0,即将static变量初始化为0了*/
-
.bss : { *(.bss) *(COMMON) } /*全局变量和static变量一起,放在bss段中*/
-
__bss_end = .;
-
}
-
-
-
/*
-
加上nandflash的其他函数之后, head.o init.o nand.o >4K 了,开发板只复制nand的前面4K到启动石。
-
使用下面的链接脚本会有局限性。
-
SECTIONS {
-
firtst 0x00000000 : { head.o init.o nand.o}
-
second 0x30001000 : AT(4096) { leds.o }
-
}
-
解决方法 :
-
1,如果非要使用上面的链接脚本,就必须把nand.c文件控制在很小范围内,
-
其他nand相关函数独立编写一个文件nand2.c,比如 :
-
SECTIONS {
-
firtst 0x00000000 : { head.o init.o nand.o}
-
second 0x30001000 : AT(4096) { leds.o nand2.o }
-
}
-
2,使用本文件使用的链接脚本,只要保证head.S中bl调用的少量函数(init_uart, nand_init, nand_read)在4K范围内,就可以了
-
整个.o文件可以完全大于4K范围。
-
*/
oled.c文件
-
/*
-
* FILE: spi.c
-
* 用于主机发送/接收
-
*/
-
-
#include "def.h"
-
#include "init.h"
-
#include "nand.h"
-
#include "uart.h"
-
#include "string.h"
-
#include "setup.h"
-
#include "interrupt.h"
-
-
#include "spi.h"
-
#include "oled.h"
-
#include "oledfont.h"
-
-
/*
-
*有机发光二极管又称为有机电激光显示(Organic Light-Emitting Diode,OLED)
-
*芯片手册 : SPEC UG-2864TMBEG01 --OLED数据手册
-
*这款OLED芯片可以使用很多种接口来通讯,其中就包括SPI
-
*/
-
-
/*
-
*函数功能 : 设置 OLED的命令|数据引脚 0 : 命令引脚 1 : 数据引脚
-
*/
-
void oled_set_dc(unsigned char val)
-
{
-
if(val == 1) //让 OLED的命令|数据引脚 输出高电平
-
{
-
GPFDAT |= (1<<3); //GPF3输出高电平
-
}
-
else if(val == 0) //让 OLED的命令|数据引脚 输出低电平
-
{
-
GPFDAT &= (~(1<<3)); //GPF3输出低电平
-
}
-
}
-
-
/*
-
*函数功能 : 设置 OLED片选引脚 0:选中 1:不选中
-
*/
-
void oled_set_cs(unsigned char val)
-
{
-
if(val == 1) //让 OLED的命令|数据引脚 输出高电平
-
{
-
GPGDAT |= (1<<1); //GPG1输出高电平
-
}
-
else if(val == 0) //让 OLED的命令|数据引脚 输出低电平
-
{
-
GPGDAT &= (~(1<<1)); //GPG1输出低电平
-
}
-
}
-
-
/*
-
*函数功能 : 通过SPI引脚,把命令cmd发送到OLED硬件上面去
-
*/
-
void oled_write_cmd(unsigned char cmd)
-
{
-
//uart_printf("oled_write_cmd \n");
-
oled_set_dc(0); //设置 OLED的命令|数据引脚,0 : 命令引脚
-
oled_set_cs(0); //设置 OLED片选引脚 0:选中
-
-
spi_send_byte(cmd); //通过SPI引脚发送数据
-
-
oled_set_cs(1); //设置 OLED片选引脚 1:不选中
-
oled_set_dc(1); //操作完成之后,恢复为 1 : 数据引脚
-
}
-
-
-
/*
-
*函数功能 : 通过SPI引脚,把数据data发送到OLED硬件上面去
-
*/
-
void oled_write_data(unsigned char data)
-
{
-
oled_set_dc(1); //设置 OLED的命令|数据引脚,1 : 数据引脚
-
oled_set_cs(0); //设置 OLED片选引脚 0:选中
-
-
spi_send_byte(data); //通过SPI引脚发送数据
-
-
oled_set_cs(1); //设置 OLED片选引脚 1:不选中
-
oled_set_dc(1); //操作完成之后,继续为 1 : 数据引脚
-
}
-
-
/*
-
*函数功能 : oled全部点亮
-
*注意 : 把8页全部写入1即可
-
*/
-
void oled_all_light(void)
-
{
-
int page, i;
-
for (page = 0; page < 8; page ++)
-
{
-
oled_set_addr(page, 0);
-
for (i = 0; i < 128; i++)
-
{
-
oled_write_data(1);
-
}
-
}
-
}
-
-
/*
-
*函数功能 : oled清屏
-
*注意 : 把8页全部写入0即可
-
*/
-
void oled_clear(void)
-
{
-
int page;
-
int i;
-
for(page=0; page<8; page++)
-
{
-
oled_set_addr(page, 0); //选中第page页,第0列
-
for(i=0; i<128; i++) //128列
-
{
-
oled_write_data(0);
-
}
-
}
-
}
-
-
/*
-
*函数功能 : oled清1页
-
*注意 : 把这页全部写入0即可
-
*/
-
void oled_clear_page(int page)
-
{
-
int i;
-
-
oled_set_addr(page, 0); //选中第page页,第0列
-
for(i=0; i<128; i++) //128列
-
{
-
oled_write_data(0);
-
}
-
}
-
-
/*
-
*函数功能 : 设置oled处于页地址模式
-
*在数据手册的第30页
-
*/
-
void oled_set_page_addr_mode(void)
-
{
-
oled_write_cmd(0x20);
-
oled_write_cmd(0x02);
-
}
-
-
/*
-
*函数功能 : OLED芯片的初始化
-
*在数据手册的第29页,有初始化方法
-
*/
-
void oled_init(void)
-
{
-
/* 向OLED发命令以初始化 */
-
oled_write_cmd(0xAE); /*display off*/
-
oled_write_cmd(0x00); /*set lower column address*/
-
oled_write_cmd(0x10); /*set higher column address*/
-
oled_write_cmd(0x40); /*set display start line*/
-
oled_write_cmd(0xB0); /*set page address*/
-
oled_write_cmd(0x81); /*contract control*/
-
oled_write_cmd(0x66); /*128*/
-
oled_write_cmd(0xA1); /*set segment remap*/
-
oled_write_cmd(0xA6); /*normal / reverse*/
-
oled_write_cmd(0xA8); /*multiplex ratio*/
-
oled_write_cmd(0x3F); /*duty = 1/64*/
-
oled_write_cmd(0xC8); /*Com scan direction*/
-
oled_write_cmd(0xD3); /*set display offset*/
-
oled_write_cmd(0x00);
-
oled_write_cmd(0xD5); /*set osc division*/
-
oled_write_cmd(0x80);
-
oled_write_cmd(0xD9); /*set pre-charge period*/
-
oled_write_cmd(0x1f);
-
oled_write_cmd(0xDA); /*set COM pins*/
-
oled_write_cmd(0x12);
-
oled_write_cmd(0xdb); /*set vcomh*/
-
oled_write_cmd(0x30);
-
oled_write_cmd(0x8d); /*set charge pump enable*/
-
oled_write_cmd(0x14);
-
-
oled_set_page_addr_mode(); //设置oled处于页地址模式,默认也是处于页地址模式
-
oled_clear(); //oled清屏
-
-
oled_write_cmd(0xAF); /*display ON*/
-
}
-
-
/*
-
*函数功能 : 找到oled显示屏,设置列地址+页地址。
-
*函数参数 :
-
* col : 0到127 列位置
-
* page : 0到7 页位置
-
*注意看数据手册 : 第30页+31页,在页模式下,设置页的起始地址
-
*/
-
void oled_set_addr(unsigned int page, unsigned int col)
-
{
-
//uart_printf("oled_set_addr \n");
-
oled_write_cmd(0xB0 + page); //设置页地址
-
-
oled_write_cmd(col & 0xf); //先设置低4位列地址
-
oled_write_cmd(0x10 + (col >> 4)); //再设置高4位列地址
-
}
-
-
/*
-
*函数功能 : 向oled显示屏,指定的列地址+页地址,显示一个字符。
-
*函数参数 :
-
* col : 0到127 val表示的字符,将要显示的列位置
-
* page : 0到7 val表示的字符,将要显示的页位置
-
*注意 : 一个字模跨2个page
-
*/
-
void oled_fix_put_char(unsigned int page, unsigned int col, unsigned char val)
-
{
-
int i;
-
/*得到字模*/
-
const unsigned char *zifu = oled_asc2_8x16[val - ' ']; //val - ' ' : 字符在字模数组中对应的下标
-
-
/*发给oled*/
-
//uart_printf("oled_fix_put_char page = [%d], col = [%d], val = [%c]\n\n", page, col, val);
-
oled_set_addr(page, col); //设置位置,字符所占的第1个page
-
/* 发出8字节数据 */
-
for (i = 0; i < 8; i++)
-
{
-
oled_write_data(zifu[i]); //连续写,列地址就自动加1
-
}
-
oled_set_addr(page+1, col); //1个字符占2页,字符所占的第2个page
-
/* 发出8字节数据 */
-
for (i = 0; i < 8; i++)
-
{
-
oled_write_data(zifu[i+8]);
-
}
-
}
-
-
/*
-
*函数功能 : 向oled显示屏,打印一个字符串,从指定的行,列开始打印,会自动换行
-
*函数参数 :
-
* col : 0到127 起始存放的 第1个字符的列
-
* page : 0到7 起始存放的 第1个字符所在的页
-
*注意 :
-
* 1, 我们使用的OLED的分辨率=128*64 0:表示像素熄灭, 1:表示像素点亮
-
* 2, 显存大小 = 128*64位=1K字节
-
* 3, OLED主控芯片数据手册,SSD1306(36页)10.1.3节,设置显存的地址模式,提供了3种地址模式,
-
* 最常用的是 : 页地址模式。
-
* 4, 页地址模式:
-
* 64行分为8页,每页对应8行。可以选中指定的页,再选中指定的列,再向里面写数据,
-
* 每写1列,列地址就自动加1,1页写完之后,继续写,会自动跳到下一页开头位置继续写。
-
* 5, 约定一个数字|英文字符的大小是 8*16, 宽:8列, 高:16行。
-
* 这样,这个OLED屏,可以显示8行,每行8个字符
-
*/
-
void oled_print(unsigned int page, unsigned int col, char * str)
-
{
-
char * tmp;
-
tmp = str;
-
-
while((*tmp) != '\0')
-
{
-
//uart_printf("oled_print *tmp = [%c]\n\n", *tmp);
-
oled_fix_put_char(page, col, *tmp);
-
col += 8; //每个字符占8列
-
if(col > 127) //一行写满,自动换行
-
{
-
col = 0;
-
page += 2; //每个字符占16行,跨2页
-
if(page > 7) //8页都写满,再从最开始的位置,再写。
-
{
-
page = 0;
-
}
-
}
-
tmp++;
-
}
-
}
oled.h文件
-
#ifndef _OLED_H_
-
#define _OLED_H_
-
-
void oled_set_dc(unsigned char val);
-
void oled_set_cs(unsigned char val);
-
void oled_write_cmd(unsigned char cmd);
-
void oled_write_data(unsigned char data);
-
void oled_all_light(void);
-
void oled_clear(void);
-
void oled_clear_page(int page);
-
void oled_set_page_addr_mode(void);
-
void oled_init(void);
-
void oled_set_addr(unsigned int page, unsigned int col);
-
void oled_fix_put_char(unsigned int page, unsigned int col, unsigned char val);
-
void oled_print(unsigned int page, unsigned int col, char * str);
-
-
#endif
oledfont.h
-
#ifndef _OLEDFONT_H_
-
#define _OLEDFONT_H_
-
-
/*
-
注意 : 下面的对2维数组的定义,每个元素都被赋值了,即为对象已经分配内存了,则不能在多个文件中包含这个头文件。
-
如果仅仅是一些声明,是可以在多个文件中包含这个头文件的。
-
-
注意 : 对象必须有且只有1个定义,但是它可以有多extern声明。
-
*/
-
-
/*
-
*字模 : 8*16像素 = 宽:8位 + 高:16位
-
*注意 : 下面列出来的字符是按照ascii码从空格开始,按照顺序列出来,这么做是为了方便取码
-
*下面理解成 : 1维数组,这个数组的每个元素又是1维数组
-
*在oled屏幕中的位置:
-
* 比如字符A : 在数组中的下标是33, 则oled_asc2_8x16[33]可以表示1个1维数组名,这个1维数组可以取出16个字符
-
* {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33
-
*
-
* 上面页,8列,从最左1列开始,依次对应上面16个字符的前面8个字符 : 0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00
-
*
-
* 下面模拟OLED的1个字符的显示 像素=8*16
-
* |0|0|0|0|0|0|0|0| 字符的最低位
-
* |0|0|0|0|0|0|0|0|
-
* |0|0|0|0|0|0|0|0|
-
* |0|0|0|1|0|0|0|0|
-
* |0|0|0|1|0|0|0|0|
-
* |0|0|0|1|1|0|0|0|
-
* |0|0|1|0|1|0|0|0|
-
* _____|0|0|1|0|1|0|0|0|_____ 字符的最高位
-
* |0|0|1|0|0|1|0|0| 字符的最低位
-
* |0|0|1|1|1|1|0|0|
-
* |0|1|0|0|0|1|0|0|
-
* |0|1|0|0|0|0|1|0|
-
* |0|1|0|0|0|0|1|0|
-
* |1|1|1|0|0|1|1|1|
-
* |0|0|0|0|0|0|0|0|
-
* |0|0|0|0|0|0|0|0| 字符的最高位
-
* 下面页,8列,从最左1列开始,依次对应上面16个字符的后面8个字符 ; 0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20
-
* 说明 : 所有标1的地方,看起来是不是很像大写的字符A ???
-
*/
-
const unsigned char oled_asc2_8x16[95][16]=
-
{
-
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// 0 数组下标 = 对应字符的asc2码 - 空格的asc码
-
{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},//!1
{0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//"2
{0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00},//#3
{0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00},//$4
{0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00},//%5
{0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10},//&6
{0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//'7
{0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},//(8
{0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},//)9
{0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},//*10
{0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00},//+11
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00},//,12
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01},//-13
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},//.14
{0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00},///15
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},//016
{0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//117
{0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},//218
{0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},//319
{0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},//420
{0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},//521
{0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},//622
{0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},//723
{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},//824
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00},//925
{0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},//:26
{0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00},//;27
{0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},//<28
{0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00},//=29
{0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},//>30
{0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00},//?31
{0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00},//@32
{0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33
{0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},//B34
{0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},//C35
{0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},//D36
{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},//E37
{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},//F38
{0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},//G39
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},//H40
{0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//I41
{0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},//J42
{0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},//K43
{0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},//L44
{0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00},//M45
{0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},//N46
{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},//O47
{0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},//P48
{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00},//Q49
{0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},//R50
{0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},//S51
{0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//T52
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//U53
{0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},//V54
{0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00},//W55
{0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},//X56
{0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//Y57
{0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},//Z58
{0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},//[59
{0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},//\60
{0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},//]61
{0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//^62
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},//_63
{0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//`64
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20},//a65
{0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},//b66
{0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},//c67
{0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20},//d68
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00},//e69
{0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//f70
{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},//g71
{0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//h72
{0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//i73
{0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},//j74
{0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00},//k75
{0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//l76
{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},//m77
{0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//n78
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//o79
{0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00},//p80
{0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80},//q81
{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},//r82
{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},//s83
{0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00},//t84
{0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},//u85
{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00},//v86
{0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00},//w87
{0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00},//x88
{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00},//y89
{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},//z90
{0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40},//{91
{0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},//|92
{0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00},//}93
{0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//~94
};
#endif
-
setup.c
-
#include "setup.h"
-
-
/*
-
*功能:使能ICache
-
*/
-
void icache_enable()
-
{
-
unsigned int temp = 1<<12;
-
-
asm(
-
"mrc p15,0,r0,c1,c0,0\n" /*读出控制寄存器c1的值到r0中*/
-
"orr r0,r0,%0\n" /*第12位I位置1,即使能ICache*/
-
"mcr p15,0,r0,c1,c0,0\n" /*将修改后的值写入控制寄存器*/
-
:
-
:"r"(temp)
-
:"r0"
-
);
-
}
-
-
-
/*
-
*功能:禁止ICache
-
*/
-
void icache_disable()
-
{
-
unsigned int temp = 1<<12;
-
-
asm( /*GCC内联汇编函数*/
-
"mrc p15,0,r0,c1,c0,0\n"
-
"bic r0,r0,%0\n" /*第12位I位置0,即禁止ICache*/
-
"mcr p15,0,r0,c1,c0,0\n"
-
:
-
:"r"(temp)
-
:"r0"
-
);
-
}
setup.h文件
-
#ifndef _SETUP_H_
-
#define _SETUP_H_
-
-
void icache_enable();
-
void icache_disable();
-
-
#endif
sp.c文件
-
/*
-
* FILE: spi.c
-
* 用于主机发送/接收
-
*/
-
-
#include "def.h"
-
#include "init.h"
-
#include "nand.h"
-
#include "string.h"
-
#include "uart.h"
-
#include "setup.h"
-
#include "interrupt.h"
-
-
#include "spi.h"
-
#include "oled.h"
-
#include "flash.h"
-
-
/*是使用SPI控制器来控制spi的引脚
-
*本程序使用查询模式,没有使用中断模式
-
*/
-
-
/*
-
*函数功能 : 通过spi mosi引脚发送 一个字节的数据
-
*注意 : 采用的是格式A(s3c2440数据手册的361页)来发送数据
-
*/
-
void spi_send_byte(unsigned char val)
-
{
-
icache_disable(); //降低频率
-
while (!(SPSTA0 & 1));
-
SPTDAT0 = val;
-
icache_enable(); //恢复频率到 PCLK=50MHZ
-
}
-
-
/*
-
*函数功能 : 通过spi miso引脚读取 一个字节的数据
-
*函数返回值 : 读取到的值
-
*芯片手册 : W25Q16DV-具有SPI接口的flash芯片 27页
-
* 上面写的DI:是对flash的输入,对2440来说是DO:数据输出
-
* 上面写的DO:是对flash的输出,对2440来说是DI:数据输入
-
*注意 : 发送完命令+地址之后,spi_miso引脚上,已经出现了可以读取的数据,已经开始等待2440来读取了。
-
*/
-
unsigned char spi_recv_byte(void)
-
{
-
icache_disable(); //降低频率
-
SPTDAT0 = 0xff; /*数据手册s3c2440的360页 : 实际上是产生8个脉冲。产生脉冲的同时,spi控制器会把数据读取出来。*/
-
while (!(SPSTA0 & 1));
-
icache_enable(); //恢复频率到 PCLK=50MHZ
-
return SPRDAT0;
-
}
-
-
-
/*
-
*函数功能 : SPI控制器的初始化
-
*注意 : s3c2440有2个SPI控制器,2个通道:通道0+通道1
-
* tq2440,使用的是 GPE11,12,13口的SPI,是通道0。我们本程序使用的SPI模块是接这个
-
* GPG5,6,7口的SPI+GPD8,9,10口的SPI,是通道1
-
*/
-
void spi_cont_init(void)
-
{
-
//1,OLED片选引脚 OLED_CSn :GPG1,设置为输出
-
GPGCON &= GPG1_CLEAN_0; //为了保持其他位不变,只清0相关的2个位
-
GPGCON |= GPG1_OUT; //为了保持其他位不变,只对相关的2个位赋值
-
GPGDAT |= (1<<1); //先不片选
-
-
//2,FLASH片选引脚 FLASH_CSn :GPG10,设置为输出
-
GPGCON &= GPG10_CLEAN_0;
-
GPGCON |= GPG10_OUT;
-
GPGDAT |= (1<<10); //先不片选
-
-
//3,OLED的命令|数据引脚 OLED_DC :GPF3,设置为输出
-
GPFCON &= GPF3_CLEAN_0;
-
GPFCON |= GPF3_OUT;
-
-
//下面才是SPI控制器相关的设置,上面是其他接口芯片的设置
-
//4,时钟引脚 : GPE13,设置为 10:SPICLK0 引脚
-
GPECON &= GPE13_CLEAN_0; //清0
-
GPECON |= (2<<(13*2));
-
-
//5,主机数据输出引脚 : GPE12,设置为 10:SPIMOSI 引脚
-
GPECON &= GPE12_CLEAN_0; //清0
-
GPECON |= (2<<(12*2));
-
-
//6,主机数据输入引脚 : GPE11,设置为 10:SPIMISO 引脚
-
GPECON &= GPE11_CLEAN_0; //清0
-
GPECON |= (2<<(11*2));
-
-
/*7, 波特率寄存器*/
-
/* OLED : 100ns, 10MHz //SPEC UG-2864TMBEG01 --OLED成品的数据手册 4线spi 17页
-
* FLASH : 104MHz 3.3V //W25Q16DV-具有SPI接口的flash芯片 66页
-
* 取10MHz
-
* 10 = 50M / 2 / (Prescaler value + 1) //2440最大提供25M频率
-
* Prescaler value = 1.5 取 2
-
* Baud rate = 50/2/3=8.3MHz //真实波特率
-
*/
-
SPPRE0 = 2;
-
SPPRE1 = 2;
-
-
/*8, SPI控制寄存器*/
-
/*[6:5] : 00, polling mode 查询模式
-
* [4] : 1 = enable 使能时钟
-
* [3] : 1 = master 主机控制器
-
* [2] : CPOL位 0 = 高电平有效,在没有SPI的操作时,时钟线的电平
-
* [1] : CPHA位 0 = format A 格式A
-
* [0] : 0 = normal mode
-
*/
-
SPCON0 = (1<<4) | (1<<3);
-
SPCON1 = (1<<4) | (1<<3);
-
}
-
-
/*
-
在s3c2440数据手册,英文版501页
-
1,CPOL : 时钟的电平的初始值
-
0 : SPICLK = 0
-
1 : SPICLK = 1
-
2, CPHA : 相位
-
0 : 在SPICLK第1个时钟沿,即第1个时钟跳变的时候,采样数据
-
1 : 在SPICLK第2个时钟沿,即第2个时钟跳变的时候,采样数据
-
注意 :
-
经常使用 mode0 : 00 mode3 : 11
-
因为他们都是在SPICLK的上升沿采样即可,不用管SPICLK的初始值,也不用再管是在SPICLK的第几个时钟沿。
-
-
*/
spi.h文件
-
#ifndef _SPI_H_
-
#define _SPI_H_
-
-
/* SPI */
-
#define SPCON0 (*(volatile unsigned long *)0x59000000)
-
#define SPSTA0 (*(volatile unsigned long *)0x59000004)
-
#define SPPIN0 (*(volatile unsigned long *)0x59000008)
-
#define SPPRE0 (*(volatile unsigned long *)0x5900000C)
-
#define SPTDAT0 (*(volatile unsigned char *)0x59000010)
-
#define SPRDAT0 (*(volatile unsigned char *)0x59000014)
-
-
#define SPCON1 (*(volatile unsigned long *)0x59000020)
-
#define SPSTA1 (*(volatile unsigned long *)0x59000024)
-
#define SPPIN1 (*(volatile unsigned long *)0x59000028)
-
#define SPPRE1 (*(volatile unsigned long *)0x5900002C)
-
#define SPTDAT1 (*(volatile unsigned char *)0x59000030)
-
#define SPRDAT1 (*(volatile unsigned char *)0x59000034)
-
-
/* TQ2440开发板中,用来模拟spi引脚的gpio引脚 相关的寄存器 */
-
#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 GPG1_CLEAN_0 (~(3<<(1*2))) //对应的2位清0
-
#define GPG1_OUT (1<<(1*2)) //对应的2位设置为01,输出
-
-
#define GPG10_CLEAN_0 (~(3<<(10*2)))
-
#define GPG10_OUT (1<<(10*2))
-
-
#define GPF3_CLEAN_0 (~(3<<(3*2)))
-
#define GPF3_OUT (1<<(3*2))
-
-
#define GPE11_CLEAN_0 (~(3<<(11*2))) //对应的2位清0,也即设置为输入
-
#define GPE11_OUT (1<<(11*2))
-
-
#define GPE12_CLEAN_0 (~(3<<(12*2)))
-
#define GPE12_OUT (1<<(12*2))
-
-
#define GPE13_CLEAN_0 (~(3<<(13*2)))
-
#define GPE13_OUT (1<<(13*2))
-
-
void spi_send_byte(unsigned char val);
-
unsigned char spi_recv_byte(void);
-
void spi_cont_init(void);
-
-
#endif
string.c文件
-
#include "string.h"
-
#include "uart.h"
-
-
char * ___strtok;
-
-
/**
-
* strcpy - Copy a %NUL terminated string
-
* @dest: Where to copy the string to
-
* @src: Where to copy the string from
-
*/
-
char * strcpy(char * dest,const char *src)
-
{
-
char *tmp = dest;
-
-
while ((*dest++ = *src++) != '\0')
-
/* nothing */;
-
return tmp;
-
}
-
-
/**
-
* strncpy - Copy a length-limited, %NUL-terminated string
-
* @dest: Where to copy the string to
-
* @src: Where to copy the string from
-
* @count: The maximum number of bytes to copy
-
*
-
* Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
-
* However, the result is not %NUL-terminated if the source exceeds
-
* @count bytes.
-
*/
-
char * strncpy(char * dest,const char *src,unsigned int count)
-
{
-
char *tmp = dest;
-
-
while (count-- && (*dest++ = *src++) != '\0')
-
/* nothing */;
-
-
return tmp;
-
}
-
-
/**
-
* strcat - Append one %NUL-terminated string to another
-
* @dest: The string to be appended to
-
* @src: The string to append to it
-
*/
-
char * strcat(char * dest, const char * src)
-
{
-
char *tmp = dest;
-
-
while (*dest)
-
dest++;
-
while ((*dest++ = *src++) != '\0')
-
;
-
-
return tmp;
-
}
-
-
/**
-
* strncat - Append a length-limited, %NUL-terminated string to another
-
* @dest: The string to be appended to
-
* @src: The string to append to it
-
* @count: The maximum numbers of bytes to copy
-
*
-
* Note that in contrast to strncpy, strncat ensures the result is
-
* terminated.
-
*/
-
char * strncat(char *dest, const char *src, unsigned int count)
-
{
-
char *tmp = dest;
-
-
if (count) {
-
while (*dest)
-
dest++;
-
while ((*dest++ = *src++)) {
-
if (--count == 0) {
-
*dest = '\0';
-
break;
-
}
-
}
-
}
-
-
return tmp;
-
}
-
-
/**
-
* strcmp - Compare two strings
-
* @cs: One string
-
* @ct: Another string
-
*/
-
int strcmp(const char * cs,const char * ct)
-
{
-
register signed char __res;
-
-
while (1) {
-
if ((__res = *cs - *ct++) != 0 || !*cs++)
-
break;
-
}
-
-
return __res;
-
}
-
-
/**
-
* strncmp - Compare two length-limited strings
-
* @cs: One string
-
* @ct: Another string
-
* @count: The maximum number of bytes to compare
-
*/
-
int strncmp(const char * cs,const char * ct,unsigned int count)
-
{
-
register signed char __res = 0;
-
-
while (count) {
-
if ((__res = *cs - *ct++) != 0 || !*cs++)
-
break;
-
count--;
-
}
-
-
return __res;
-
}
-
-
/**
-
* strchr - Find the first occurrence of a character in a string
-
* @s: The string to be searched
-
* @c: The character to search for
-
*/
-
char * strchr(const char * s, int c)
-
{
-
for(; *s != (char) c; ++s)
-
if (*s == '\0')
-
return NULL;
-
return (char *) s;
-
}
-
-
/**
-
* strrchr - Find the last occurrence of a character in a string
-
* @s: The string to be searched
-
* @c: The character to search for
-
*/
-
char * strrchr(const char * s, int c)
-
{
-
const char *p = s + strlen(s);
-
do {
-
if (*p == (char)c)
-
return (char *)p;
-
} while (--p >= s);
-
return NULL;
-
}
-
-
/**
-
* strlen - Find the length of a string
-
* @s: The string to be sized
-
*/
-
unsigned int strlen(const char * s)
-
{
-
const char *sc;
-
-
for (sc = s; *sc != '\0'; ++sc)
-
/* nothing */;
-
return sc - s;
-
}
-
-
/**
-
* strnlen - Find the length of a length-limited string
-
* @s: The string to be sized
-
* @count: The maximum number of bytes to search
-
*/
-
unsigned int strnlen(const char * s, unsigned int count)
-
{
-
const char *sc;
-
-
for (sc = s; count-- && *sc != '\0'; ++sc)
-
/* nothing */;
-
return sc - s;
-
}
-
-
/**
-
* strspn - Calculate the length of the initial substring of @s which only
-
* contain letters in @accept
-
* @s: The string to be searched
-
* @accept: The string to search for
-
*/
-
unsigned int strspn(const char *s, const char *accept)
-
{
-
const char *p;
-
const char *a;
-
unsigned int count = 0;
-
-
for (p = s; *p != '\0'; ++p) {
-
for (a = accept; *a != '\0'; ++a) {
-
if (*p == *a)
-
break;
-
}
-
if (*a == '\0')
-
return count;
-
++count;
-
}
-
-
return count;
-
}
-
-
/**
-
* strpbrk - Find the first occurrence of a set of characters
-
* @cs: The string to be searched
-
* @ct: The characters to search for
-
*/
-
char * strpbrk(const char * cs,const char * ct)
-
{
-
const char *sc1,*sc2;
-
-
for( sc1 = cs; *sc1 != '\0'; ++sc1) {
-
for( sc2 = ct; *sc2 != '\0'; ++sc2) {
-
if (*sc1 == *sc2)
-
return (char *) sc1;
-
}
-
}
-
return NULL;
-
}
-
-
/**
-
* strtok - Split a string into tokens
-
* @s: The string to be searched
-
* @ct: The characters to search for
-
*
-
* WARNING: strtok is deprecated, use strsep instead.
-
*/
-
char * strtok(char * s,const char * ct)
-
{
-
char *sbegin, *send;
-
-
sbegin = s ? s : ___strtok;
-
if (!sbegin) {
-
return NULL;
-
}
-
sbegin += strspn(sbegin,ct);
-
if (*sbegin == '\0') {
-
___strtok = NULL;
-
return( NULL );
-
}
-
send = strpbrk( sbegin, ct);
-
if (send && *send != '\0')
-
*send++ = '\0';
-
___strtok = send;
-
return (sbegin);
-
}
-
-
/**
-
* strsep - Split a string into tokens
-
* @s: The string to be searched
-
* @ct: The characters to search for
-
*
-
* strsep() updates @s to point after the token, ready for the next call.
-
*
-
* It returns empty tokens, too, behaving exactly like the libc function
-
* of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
-
* Same semantics, slimmer shape. ;)
-
*/
-
char * strsep(char **s, const char *ct)
-
{
-
char *sbegin = *s, *end;
-
-
if (sbegin == NULL)
-
return NULL;
-
-
end = strpbrk(sbegin, ct);
-
if (end)
-
*end++ = '\0';
-
*s = end;
-
-
return sbegin;
-
}
-
-
/**
-
* memset - Fill a region of memory with the given value
-
* @s: Pointer to the start of the area.
-
* @c: The byte to fill the area with
-
* @count: The size of the area.
-
*
-
* Do not use memset() to access IO space, use memset_io() instead.
-
*/
-
void * memset(void * s,int c,unsigned int count)
-
{
-
char *xs = (char *) s;
-
-
while (count--)
-
*xs++ = c;
-
-
return s;
-
}
-
-
/**
-
* bcopy - Copy one area of memory to another
-
* @src: Where to copy from
-
* @dest: Where to copy to
-
* @count: The size of the area.
-
*
-
* Note that this is the same as memcpy(), with the arguments reversed.
-
* memcpy() is the standard, bcopy() is a legacy BSD function.
-
*
-
* You should not use this function to access IO space, use memcpy_toio()
-
* or memcpy_fromio() instead.
-
*/
-
void bcopy(const void *src, void *dest, unsigned int count)
-
{
-
char *destTmp = (char *)dest;
-
char *srcTmp = (char *)src;
-
-
while (count--)
-
*destTmp++ = *srcTmp++;
-
}
-
-
/**
-
* memcpy - Copy one area of memory to another
-
* @dest: Where to copy to
-
* @src: Where to copy from
-
* @count: The size of the area.
-
*
-
* You should not use this function to access IO space, use memcpy_toio()
-
* or memcpy_fromio() instead.
-
*/
-
void * memcpy(void * dest,const void *src,unsigned int count)
-
{
-
char *tmp = (char *) dest, *s = (char *) src;
-
-
while (count--)
-
*tmp++ = *s++;
-
-
return dest;
-
}
-
-
/**
-
* memmove - Copy one area of memory to another
-
* @dest: Where to copy to
-
* @src: Where to copy from
-
* @count: The size of the area.
-
*
-
* Unlike memcpy(), memmove() copes with overlapping areas.
-
*/
-
void * memmove(void * dest,const void *src,unsigned int count)
-
{
-
char *tmp, *s;
-
-
if (dest <= src) {
-
tmp = (char *) dest;
-
s = (char *) src;
-
while (count--)
-
*tmp++ = *s++;
-
}
-
else {
-
tmp = (char *) dest + count;
-
s = (char *) src + count;
-
while (count--)
-
*--tmp = *--s;
-
}
-
-
return dest;
-
}
-
-
/**
-
* memcmp - Compare two areas of memory
-
* @cs: One area of memory
-
* @ct: Another area of memory
-
* @count: The size of the area.
-
*/
-
int memcmp(const void * cs,const void * ct,unsigned int count)
-
{
-
const unsigned char *su1, *su2;
-
int res = 0;
-
-
for( su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--)
-
if ((res = *su1 - *su2) != 0)
-
break;
-
return res;
-
}
-
-
/**
-
* memscan - Find a character in an area of memory.
-
* @addr: The memory area
-
* @c: The byte to search for
-
* @size: The size of the area.
-
*
-
* returns the address of the first occurrence of @c, or 1 byte past
-
* the area if @c is not found
-
*/
-
void * memscan(void * addr, int c, unsigned int size)
-
{
-
unsigned char * p = (unsigned char *) addr;
-
-
while (size) {
-
if (*p == c)
-
return (void *) p;
-
p++;
-
size--;
-
}
-
return (void *) p;
-
}
-
-
/**
-
* strstr - Find the first substring in a %NUL terminated string
-
* @s1: The string to be searched
-
* @s2: The string to search for
-
*/
-
char * strstr(const char * s1,const char * s2)
-
{
-
int l1, l2;
-
-
l2 = strlen(s2);
-
if (!l2)
-
return (char *) s1;
-
l1 = strlen(s1);
-
while (l1 >= l2) {
-
l1--;
-
if (!memcmp(s1,s2,l2))
-
return (char *) s1;
-
s1++;
-
}
-
return NULL;
-
}
-
-
/**
-
* memchr - Find a character in an area of memory.
-
* @s: The memory area
-
* @c: The byte to search for
-
* @n: The size of the area.
-
*
-
* returns the address of the first occurrence of @c, or %NULL
-
* if @c is not found
-
*/
-
void *memchr(const void *s, int c, unsigned int n)
-
{
-
const unsigned char *p = (const unsigned char *)s;
-
while (n-- != 0) {
-
if ((unsigned char)c == *p++) {
-
return (void *)(p-1);
-
}
-
}
-
return NULL;
-
}
-
-
/*功能:向字符串 格式化打印一个字符串
-
*参数:格式化的字符串
-
*注意:这个是简易版本 (%02x 没法完成)
-
-
typedef char * va_list;
-
#define va_start(ap,p) (ap = (char *) (&(p)+1))
-
#define va_arg(ap, type) ((type *)(ap += sizeof(type)))[-1]
-
#define va_end(ap)
-
-
*/
-
int sprintf(char * str, const char *fmt, ...)
-
{
-
int count = 0;
-
char c;
-
char *s;
-
int n;
-
char buf[65];
-
va_list ap;
-
-
va_start(ap, fmt);
-
-
while(*fmt != '\0')
-
{
-
if(*fmt == '%')
-
{
-
fmt++;
-
switch(*fmt)
-
{
-
case 'd': /*整型*/
-
n = va_arg(ap, int);
-
if(n < 0)
-
{
-
*str = '-';
-
str++;
-
n = -n;
-
}
-
itoa(n, buf);
-
memcpy(str, buf, strlen(buf));
-
str += strlen(buf);
-
-
break;
-
case 'c': /*字符型*/
-
c = va_arg(ap, int);
-
*str = c;
-
str++;
-
-
break;
-
case 'x': /*16进制*/
-
n = va_arg(ap, int);
-
xtoa(n, buf);
-
memcpy(str, buf, strlen(buf));
-
str += strlen(buf);
-
-
break;
-
case 's': /*字符串*/
-
s = va_arg(ap, char *);
-
memcpy(str, s, strlen(s));
-
str += strlen(s);
-
-
break;
-
case '%': /*输出%*/
-
*str = '%';
-
str++;
-
-
break;
-
default:
-
break;
-
}
-
}
-
else
-
{
-
*str = *fmt;
-
str++;
-
-
if(*fmt == '\n')
-
{
-
//uart_sendByte('\r');
-
}
-
}
-
fmt++;
-
}
-
-
va_end(ap);
-
-
return count;
-
}
-
-
-
/*
-
*功能:整型(int) 转化成 字符型(char)
-
*注意:不用 % / 符号的话,只能正确打印:0...9的数字对应的字符'0'...'9'
-
*/
-
void itoa(unsigned int n, char * buf)
-
{
-
int i;
-
-
if(n < 10)
-
{
-
buf[0] = n + '0';
-
buf[1] = '\0';
-
return;
-
}
-
itoa(n / 10, buf);
-
-
for(i=0; buf[i]!='\0'; i++);
-
-
buf[i] = (n % 10) + '0';
-
-
buf[i+1] = '\0';
-
}
-
-
/*
-
*功能:字符型(char) 转化成 整型(int)
-
*/
-
int atoi(char* pstr)
-
{
-
int int_ret = 0;
-
int int_sign = 1; //正负号标示 1:正数 -1:负数
-
-
if(pstr == '\0') //判断指针是否为空
-
{
-
uart_printf("Pointer is NULL\n");
-
return -1;
-
}
-
while(((*pstr) == ' ') || ((*pstr) == '\n') || ((*pstr) == '\t') || ((*pstr) == '\b'))
-
{
-
pstr++; //跳过前面的空格字符
-
}
-
-
/*
-
* 判断正负号
-
* 如果是正号,指针指向下一个字符
-
* 如果是符号,把符号标记为Integer_sign置-1,然后再把指针指向下一个字符
-
*/
-
if(*pstr == '-')
-
{
-
int_sign = -1;
-
}
-
if(*pstr == '-' || *pstr == '+')
-
{
-
pstr++;
-
}
-
-
while(*pstr >= '0' && *pstr <= '9') //把数字字符串逐个转换成整数,并把最后转换好的整数赋给Ret_Integer
-
{
-
int_ret = int_ret * 10 + *pstr - '0';
-
pstr++;
-
}
-
int_ret = int_sign * int_ret;
-
-
return int_ret;
-
}
-
-
/*
-
*功能:16进制字(0x) 转化成 字符型(char)
-
*注意:不用 % / 符号的话,只能正确打印,0...9..15的数字,对应的'0'...'9''A'...'F'
-
*注意:由于编译问题,这个函数,暂时由uart_sendByte_hex()函数替代
-
*/
-
void xtoa(unsigned int n, char * buf)
-
{
-
int i;
-
-
if(n < 16)
-
{
-
if(n < 10)
-
{
-
buf[0] = n + '0';
-
}
-
else
-
{
-
buf[0] = n - 10 + 'a';
-
}
-
buf[1] = '\0';
-
return;
-
}
-
xtoa(n / 16, buf);
-
-
for(i = 0; buf[i] != '\0'; i++);
-
-
if((n % 16) < 10)
-
{
-
buf[i] = (n % 16) + '0';
-
}
-
else
-
{
-
buf[i] = (n % 16) - 10 + 'a';
-
}
-
buf[i + 1] = '\0';
-
}
-
-
/*
-
* 判断一个字符是否数字
-
*/
-
int isDigit(unsigned char c)
-
{
-
if (c >= '0' && c <= '9')
-
return 1;
-
else
-
return 0;
-
}
-
-
/*
-
* 判断一个字符是否英文字母
-
*/
-
int isLetter(unsigned char c)
-
{
-
if (c >= 'a' && c <= 'z')
-
return 1;
-
else if (c >= 'A' && c <= 'Z')
-
return 1;
-
else
-
return 0;
-
}
-
-
-
/*
-
研究数据结构的时候,还有很多字符串处理函数
-
*/
string.h文件
-
#ifndef _STRING_H_
-
#define _STRING_H_
-
-
typedef char * va_list;
-
#define va_start(ap,p) (ap = (char *) (&(p)+1))
-
#define va_arg(ap, type) ((type *) (ap += sizeof(type)))[-1]
-
//#define va_argp(ap, type) ((type *) (ap += sizeof(type)))-1
-
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
-
#define va_arg(ap,type) ( *(type *)((ap += 1) - 1) )
-
#define va_end(ap)
-
-
#define NULL 0
-
-
char * strcpy(char * dest,const char *src);
-
char * strncpy(char * dest,const char *src,unsigned int count);
-
char * strcat(char * dest, const char * src);
-
char * strncat(char *dest, const char *src, unsigned int count);
-
int strcmp(const char * cs,const char * ct);
-
int strncmp(const char * cs,const char * ct,unsigned int count);
-
char * strchr(const char * s, int c);
-
char * strrchr(const char * s, int c);
-
unsigned int strlen(const char * s);
-
unsigned int strnlen(const char * s, unsigned int count);
-
unsigned int strspn(const char *s, const char *accept);
-
char * strpbrk(const char * cs,const char * ct);
-
char * strtok(char * s,const char * ct);
-
char * strsep(char **s, const char *ct);
-
void * memset(void * s,int c,unsigned int count);
-
void bcopy(const void *src, void *dest, unsigned int count);
-
void * memcpy(void * dest,const void *src,unsigned int count);
-
void * memmove(void * dest,const void *src,unsigned int count);
-
int memcmp(const void * cs,const void * ct,unsigned int count);
-
void * memscan(void * addr, int c, unsigned int size);
-
char * strstr(const char * s1,const char * s2);
-
void *memchr(const void *s, int c, unsigned int n);
-
int sprintf(char * str, const char *fmt, ...);
-
void itoa(unsigned int n, char * buf);
-
int atoi(char* pstr);
-
void xtoa(unsigned int n, char * buf);
-
int isDigit(unsigned char c);
-
int isLetter(unsigned char c);
-
-
#endif
uart.c文件
-
#include "string.h"
-
#include "uart.h"
-
-
-
/***********************************************************************************/
-
//串口相关函数
-
/*功能:初始化串口UART0
-
*轮询方式来使用串口
-
*/
-
void init_uart(void)
-
{
-
/*GPHCON寄存器
-
[7:6]=10,GPH2作为TXD0,串口发送数据引脚
-
[5:4]=10,GPH3作为RXD0,串口接受数据引脚
-
GPH2,GPH3控制串口通道0
-
GPH4,GPH5控制串口通道1
-
GPH6,GPH7控制串口通道2
-
*/
-
rGPHCON = ((2<<6)|(2<<4)|(0<<2)|(0<<0));
-
rGPHUP = ((1<<3)|(1<<2)); /*禁止上拉*/
-
-
/*ULCON0串口线路控制寄存器:设置传输格式
-
[6]=0,普通模式,非红外工作模式
-
[5:3]=000,无奇偶校验位,100=奇校验,101=偶校验,110=固定校验位为1,111=固定校验位为0
-
[2]=0,每帧1个停止位,1:每帧2个停止位
-
[1:0]=11,8位,每帧发送或接受的数据位的个数。00=5位,01=6位,10=7位
-
*/
-
rULCON0 = 0x3;
-
-
/*UCON0:串口控制寄存器:用于选择UART时钟源,设置UART中断方式
-
[15:12]: 当[11:10]选择PCLK时,这4位无效。选择FCLK/n时,表示n值。
-
[11:10]=00: 选择PCLK给UART比特率,10:PCLK, 01:UEXTCLK, 11:FCLK/n。
-
[9]=1: 中断请求类型
-
[8]=0: 中断请求类型
-
[7]=0: 禁止超时中断,1:使能超时中断
-
[6]=1: 产生接受错误状态中断,1:不产生接受错误状态中断
-
[5]=0: 正常操作,非回环模式。发送引脚发送的数据,直接到达接受引脚,一般是测试用。
-
[4]=0: 正常操作,不发送断电信号
-
[3:2]=01: 发送模式,中断或者轮询,如果中断寄存器不设置,就是轮询。
-
[1:0]=01: 接受模式,中断或者轮询
-
*/
-
rUCON0 = 0x5;
-
-
/*UFCON:串口FIFO控制寄存器:用于设置是否使用FIFO,设置各FIFO的触发阀值。
-
[7:6]=发送FIFO的触发深度,即在发送时,发送FIFO中还剩有多少个数据时,才触发中断,告诉CPU可以继续发送了。
-
[5:4]=接受FIFO的触发深度,即在接受时,接受FIFO中接受了多少个数据后,才触发中断,告诉CPU已经有接受到的数据了。
-
[3]=空
-
[2]=TX的FIFO是否复位
-
[1]=RX的FIFO是否复位
-
[0]=0,不使用FIFO,非FIFO模式,相当于接受和发送时,不使用缓冲寄存器,上面的设置无效。如果这里是1,则上面的位肯定有对应的设置。
-
*/
-
rUFCON0 = 0x0;
-
-
/*UMCON:串口MODEM控制寄存器:用于流量控制
-
[7:5]=000,规定必须为0
-
[4]=0:禁止自动流控制(AFC),即不使用流控。一般不使用这种硬件自动流控制手段。
-
[3:1]=000:规定必须为0
-
[0]=0:高电平(撤销nRTS),1:低电平(激活nRTS),因为,AFC位禁止,nRTS必须由软件控制。如果AFC使能,则忽略这个位。
-
*/
-
rUMCON0 = 0x0;
-
-
/*UBRDIV:波特率分频寄存器:用于设置波特率
-
[15:0],波特率分频值。使用UEXTCLK作为输入时钟时,可以设置UBRDIV为0.
-
UBRDIV = (int)(UART时钟/(波特率*16))-1
-
*/
-
rUBRDIV0 = UART_BRD;
-
}
-
-
/*功能:向串口发送一个字符
-
*参数:待发送的字符
-
-
UTRSTAT0:用来表明数据是否已经发送完毕,是否已经接受到数据
-
串口收发TX/RX状态寄存器,是CPU自动更新的,程序员检测它的状态就行。
-
[2]:发送移位寄存器空标志。
-
检测为0:表示发送移位寄存器非空,或发送缓冲寄存器非空。
-
检测为1:表示发送移位寄存器为空,且发送缓冲寄存器为空,程序员可以继续发送数据了。
-
[1]:发送缓冲区空标志位。
-
检测为0:表示发送缓冲寄存器非空,即上次要发送的数据还没有发完。不能太快,否则会覆盖掉。
-
检测为1:表示发送缓冲寄存器为空,程序员可以继续发送数据了。(非FIFO模式,非请求中断,非DMA)
-
注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。
-
[0]:接收缓冲器数据就绪标志位。
-
检测为0:表示接收缓冲器为空。
-
检测为1:表示接收缓冲器接收到有效数据,程序员可以取数据了。(非FIFO模式,非请求中断,非DMA)
-
注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。
-
-
UTXH0:串口发送缓冲寄存器
-
[7:0]:串口要发送的数据
-
*/
-
void uart_sendByte(int data)
-
{
-
if(data == '\n')
-
{
-
while(!(rUTRSTAT0 &0x2));
-
WrUTXH0('\r'); /*回车不换行*/
-
}
-
/*等待,直到发送缓冲区中的数据已经全部发送出去
-
这里也可以检测UTRSTAT0[2]位*/
-
while(!(rUTRSTAT0 &0x2));
-
/*向UTXH0寄存器中写入数据,UART即自动将它发送出去
-
#define rUTXH0 (*(volatile unsigned char *)0x50000020) //UART 0 Transmission Hold
-
#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
-
所以:WrUTXH0(data); 相当于 rUTXH0 = data;
-
*/
-
WrUTXH0(data);
-
}
-
-
/*功能:向串口打印一个字符串
-
*参数:待打印的字符串
-
*/
-
void uart_sendString(char *p)
-
{
-
while(*p)
-
uart_sendByte(*p++);
-
}
-
-
/*功能:向串口打印一个字符的16进制格式
-
*参数:待打印的字符
-
* 数字0=0x30,数字9=0x39
-
*/
-
void uart_sendByte_hex(unsigned long val)
-
{
-
// val = 0x1234ABCD
-
unsigned long c;
-
int i = 0;
-
-
uart_sendByte('0');
-
uart_sendByte('x');
-
-
for (i = 0; i < 8; i++)
-
{
-
c = (val >> ((7-i)*4)) & 0xf;
-
if((c >= 0) && (c <= 9))
-
{
-
c = '0' + c;
-
}
-
else if ((c >= 0xA) && (c <= 0xF))
-
{
-
c = 'A' + (c - 0xA);
-
}
-
uart_sendByte((int)c);
-
}
-
}
-
-
/*功能:从串口接受一个字符
-
*返回值:接受到的字符
-
-
UTRSTAT0: 串口收发TX/RX状态寄存器
-
[0]:接收缓冲器数据就绪标志位。
-
0:接收缓冲器为空
-
1:接收缓冲器接收到有效数据(非FIFO模式,非请求中断,非DMA)
-
注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。
-
-
URXH0:串口接受缓冲寄存器
-
[7:0]:串口接受到的数据
-
*/
-
char uart_getch(void)
-
{
-
/*等待,直到接受缓冲区中有数据*/
-
while(!(rUTRSTAT0 & 0x1));
-
/*直接读取URXH0寄存器,即可以获得接受到的数据
-
#define rURXH0 (*(volatile unsigned char *)0x50000024) //UART 0 Receive buffer
-
#define RdURXH0() (*(volatile unsigned char *)0x50000024)
-
所以:return RdURXH0(); 相当于:return rURXH0;
-
*/
-
return RdURXH0();
-
}
-
-
/*功能:从串口接受一个字符串
-
*参数:输入的字符串
-
*/
-
void uart_getString(char *string)
-
{
-
char *string2 = string;
-
char c;
-
while((c=uart_getch()) != '\r')
-
{
-
if(c == '\b')
-
{
-
if((int)string2 < (int)string)
-
{
-
//uart_printf("\b\b");
-
string--;
-
}
-
}
-
else
-
{
-
*string++ = c;
-
uart_sendByte(c);
-
}
-
}
-
*string = '\0';
-
uart_sendByte('\n');
-
}
-
-
-
/*******************************************************************************************
-
为了支持求余求模,需要:
-
1,修改makefile如下:增加libgcc的库
-
arm-linux-ld -Tuart.lds -o uart_elf $^ $(shell arm-linux-gcc -msoft-float -print-libgcc-file-name)
-
上面一句展开后,其实就是下面的一句:
-
## arm-linux-ld -Tuart.lds -o uart_elf head.o init.o uart.o leds.o /home/wangxc/linux/toolchain/gcc-3.4.5-glibc-2.3.6/bin/../lib/gcc/arm-linux/3.4.5/libgcc.a
-
2,自己手写libgcc的库。
-
这个在uart3实现
-
*******************************************************************************************/
-
-
/*功能:向串口格式化打印一个字符串
-
*参数:格式化的字符串
-
*注意:由于求模求余的问题没有解决,所以这里%d输出时,只能输出0~9,10和10以上的不能输出
-
*/
-
int uart_printf(const char *fmt, ...)
-
{
-
int count = 0;
-
char c;
-
char *s;
-
int n;
-
char buf[65];
-
va_list ap;
-
-
va_start(ap, fmt);
-
-
while(*fmt != '\0') //所有的'\n'都会输出
-
{
-
if(*fmt == '%')
-
{
-
fmt++;
-
switch(*fmt)
-
{
-
case 'd': /*整型*/
-
n = va_arg(ap, int);
-
if(n < 0)
-
{
-
//uputchar('-');
-
uart_sendByte('-');
-
n = -n;
-
}
-
itoa(n, buf);
-
//_uputs(buf);
-
uart_sendString(buf);
-
break;
-
case 'c': /*字符型*/
-
c = va_arg(ap, int);
-
uart_sendByte(c);
-
break;
-
case 'x': /*16进制*/
-
n = va_arg(ap, int);
-
//uart_sendByte_hex(n); /*由于求模求余编译有问题,所以用uart_sendByte_hex来替代下面的2行代码*/
-
xtoa(n, buf);
-
uart_sendString(buf);
-
break;
-
case 's': /*字符串*/
-
s = va_arg(ap, char *);
-
uart_sendString(s);
-
break;
-
case '%': /*输出%*/
-
uart_sendByte('%');
-
break;
-
default:
-
break;
-
}
-
}
-
else
-
{
-
uart_sendByte(*fmt); //最后一个 '\n'也会输出
-
if(*fmt == '\n')
-
{
-
//uart_sendByte('\r');
-
}
-
}
-
fmt++;
-
}
-
-
va_end(ap);
-
-
return count;
-
}
uart.h文件
-
#ifndef _UART_H_
-
#define _UART_H_
-
-
/**************************************************************************/
-
//串口相关寄存器
-
#define rULCON0 (*(volatile unsigned *)0x50000000)
-
#define rUCON0 (*(volatile unsigned *)0x50000004)
-
#define rUFCON0 (*(volatile unsigned *)0x50000008)
-
#define rUMCON0 (*(volatile unsigned *)0x5000000c)
-
#define rUTRSTAT0 (*(volatile unsigned *)0x50000010)
-
#define rUBRDIV0 (*(volatile unsigned *)0x50000028)
-
-
#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
-
#define RdURXH0() (*(volatile unsigned char *)0x50000024)
-
-
#define rGPHCON (*(volatile unsigned *)0x56000070)
-
#define rGPHUP (*(volatile unsigned *)0x56000078)
-
-
#define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz
-
#define UART_CLK PCLK // UART0的时钟源设为PCLK
-
#define UART_BAUD_RATE 115200 // 波特率
-
#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)
-
-
void init_uart(void);
-
void uart_sendByte(int data);
-
void uart_sendString(char *p);
-
void uart_sendByte_hex(unsigned long val);
-
char uart_getch(void);
-
void uart_getString(char *string);
-
int uart_printf(const char *fmt, ...);
-
-
/**************************************************************************/
-
-
#endif
阅读(1270) | 评论(0) | 转发(0) |