LPC1768开发板教程
注意:本文档基于LPC1768.h头文件和EZ1768.h头文件(在文档结尾附出)
文章目录
1.GPIO
*基础,重中之重
1.1 设置GPIO
-
PINSELX(X的值为:0 ~ 4 ,7~10)
引脚的对应寄存器位为两位(如PINSEL0 的0~1位是引脚p0.0的位置)
设置引脚对应的寄存器位为00
-
引脚 p0.Y(Y的值为0~ 31,其中0~ 15属于PINSEL0、16~31属于PINSEL1)
注意:设置引脚前请查阅数据手册
- 示例
PINSEL0 &= ~(0x3<<2);//设置p0.1引脚为GPIO
PINSEL1 &= ~(0x3<<2);//设置p0.17引脚为GPIO
/*tip:左移的值为引脚Y值的两倍*/
1.2 设置输入输出
-
FIOXDIR(X的值为0~4)
引脚的对应寄存器位为一位(如FIO0DIR 的第0位是引脚p0.0的位置)
设置FIOXDIR的值:输出置1、输出置0
-
FIOXSET、FIOXCLR(X的值为0~4)
- 输出高电平:FIOXSET置1
- 输出低电平:FIOXCLR置1
-
示例
/*设置引脚为输入*/
PINSEL0 &= ~(0x3<<2);//设置p0.1引脚为GPIO
FIO0DIR &= ~(1ul<<1);//设置p0.1为输入
/*设置引脚为输出*/
PINSEL0 &= ~(0x3<<2);//设置p0.1引脚为GPIO
FIO0DIR |= (1ul<<1);//设置p0.1为输出
FIO0SET |= (1ul<<1);//输出高电平
FIO0CLR |= (1ul<<1);//输出低电平
1.3 查看引脚值
-
FIOXPIN(X的值为0~4)
引脚的对应寄存器位为一位(如FIO0PIN 的第0位是引脚p0.0的位置)
- 值为1:高电平
- 值为0:低电平
-
示例
PinStat=FIO0PIN & (1ul<<1);//读取p0.1的引脚值存放于变量中
1.4 GPIO中断
-
IOXIntEnF、IOXIntEnR、IOXIntClr、IOXIntStatR、IOXIntStatF(X的值为0、2)
- EnF: 下降沿触发使能
- EnR: 上升沿触发使能
- Clr: 中断标志寄存器
- StatR: 上升沿中断状态
- StatF: 下升沿中断状态
-
特别注意
GPIO中断使用的是中断3的中断函数
-
示例
/*初始化化*/
void set_GPIO_interrupt(void){
PINSEL0 &= ~(0x3<<8);//设置p0.4引脚为GPIO
FIO0DIR &= ~(1ul<<4);//设置p0.4为输入
IO0IntEnF = 0x010;//设置为下降沿触发
IO0IntClr = 0xFFFFFFF;//清除中断标志
NVIC_SetPriority(EINT3_IRQn,4);//中断设置优先级
NVIC_EnableIRQ(EINT3_IRQn);//中断使能
}
/*中断函数*/
void EINT3_IRQHandler (void){//函数名固定、不可更改
//你自己的程序
IO0IntClr=0xFFFFFFF;//清除中断函数
}
/*判断消抖*/
if((FIO0PIN&0x010)==0){
myDelay(5);//延时5毫秒
if((FIO0PIN&0x010)==0){
//相关操作
}
}
2.外部中断
外部中断与GPIO中断相似,但可用引脚相较于GPIO更少。
2.1 中断引脚
外部中断引脚 | CPU引脚 | 描述 |
---|---|---|
EINT0 | P2.10 | 外部中断0引脚 |
EINT1 | P2.11 | 外部中断1引脚 |
EINT2 | P2.12 | 外部中断2引脚 |
EINT3 | P2.13 | 外部中断3引脚 |
注意:设置引脚对应的寄存器位为01。
2.2 中断触发模式

-
EXTMODE
中断方式寄存器(前4位分别对应的中断序号,如EXTMODE |=0x01:将中断0设置为边沿触发)
-
EXTPOLAR
极性控制寄存器(前4位分别对应的中断序号,如EXTPOLAR |=0x01:将中断0设置为高电平/上升沿触发)
注意:设置触发模式时,请将两个寄存器配合使用。
2.3 初始化中断
-
初始化流程
设置中断引脚 ==》设置中断方式 ==》设置中断极性 ==》清除中断标志 ==》设置优先级&使能
-
示例
/*初始化函数*/
PINSEL4 |=(0x01<<20);//设置P2.10为外部中断引脚
EXTMODE |=0x01;//设置为边沿触发模式
EXTPOLAR &=0xFE;//设置为下降沿触发
EXTINT =0x01;//清除中断标志
NVIC_SetPriority(EINT0_IRQn,3);//设置优先级(优先数为3)
NVIC_EnableIRQ(EINT0_IRQn);//中断使能函数
/*中断函数*/
void EINT0_IRQHandler(void){
EXTINT =0x01;
//你自己的部分
}
3.八段数码管
3.1 引脚介绍
-
A~G+dp
分别对应不同的位置(如下图)
-
S1~S4
位选引脚,用于切换显示的数码管。
3.2 显示方式&策略
-
显示方式(对应XMUT开发板)
- 引脚选择:高电平有效
- 显示数码管:低电平有效
-
显示数字策略
数码管四位显示需要通过快速切换位选有效来达到同时显示四个数字的效果。
流程:显示单个数码管 =》去除余晖 =》切换位选 =》显示单个数码管(循环操作)
-
示例
{0,0,0,0,0,0,1,1}//数字0 低电平显示(A~G+dp)
digital_com[]//0~3存放位选引脚、4~11存放A~dp引脚》引脚全部设为GPIO《
DL_MOD[][]//二维数组,存放对应数字的引脚值
/*去除余晖*/
void reset_D(void)={
int i;
for(i=4;i<12;i++){
pin_out(digital_com[i],HIGH);//EZ1768库函数,使对应引脚输出高电平
}
}
/*选择引脚*/
/*先将位选全部设为低电平,再将需要显示的位引脚设为高电平*/
void select_com(int n){//参数为选择的位(0~3)
int i;
for(i=0;i<4;i++){
pin_out(digital_com[i],LOW);//EZ1768库函数,输出低电平
}
switch(n){
case 0:pin_out(digital_com[0],HIGH);break;
case 1:pin_out(digital_com[1],HIGH);break;
case 2:pin_out(digital_com[2],HIGH);break;
case 3:pin_out(digital_com[3],HIGH);break;
default:break;
}
}
/*显示单个数字*/
void print_Dnum(int pin,int n){//参数pin:0~3(输出数字的位置) n:0~9(输出的数字)
int i;
int j=0;
select_com(pin);//选择显示位
reset_D();//去除余晖
for(i=4;i<12;i++){//按顺序修改数码管对应引脚的值
pin_out(digital_com[i],DL_MOD[n][j++]);//输出对应的电平(0为低,1为高)
}
}
/*显示大数字 》仅供参考《*/
/*由于数码管只有四位,采用滚动显示的手段*/
void print_digital_num(int n){//参数为数字
int result[10]={0};//限制为10位,用户可自行更改
int temp=n,i=0,j,flag=0;
int result_l=0;
if(n<0)temp=-temp;//判断是否为负数
result_l=0;//初始化数字长度变量
while(temp){//处理数字
result[result_l++]=temp%10;
temp=temp/10;
}
if(n<0){//17为对应示例中DL_MOD[][]数组中‘-’的行号,用户序根据自己的数组修改该值
result[result_l++]=17;
}
if(result_l<4)result_l=4;//判断数字长度是否超过4位
if(flag==0)temp=result_l;//初始化显示起始位置
for(j=temp-1;j>=0;j--){
reset_D();//消除余晖
print_Dnum(i++,result[j]);
}
flag++;//用于滚动显示
if(flag%5000==0)temp--;//运行5000次后对显示起始位进行修改,用户可自行修改
else if(temp<4)flag=0;
}
4. 74LS164芯片
4.1 引脚介绍
- 引脚介绍
- DSA(A)/ DSB(B):串行数据输入端
- Q0~Q7:数据输出端
- CP(clock):时钟输入端
- nMR(clear):清零端,低电平有效
- VCC(VDD):接电源
- GND:接地
- 引脚图
4.2 真值表&时序图
-
真值表
-
时序图


-
工作原理
一次能过向74LS164传输一个长8位的数据,每个上升沿传输一位。传入数据值由引脚A或者B来控制(高电平为1低电平为0)
注意:使用XMUT开发板的引脚A与B引脚经过处理,可以只接其中一个引脚
4.3 应用示例
/*引脚初始化-以A为输出端*/
set_pin_out(C_CLK,LOW);//初始化时钟为低电平
set_pin_out(C_CLR,LOW);//初始化清零为低电平
set_pin_out(C_A,LOW);//初始化数据输出端A为低电平
/**************************发送数据函数*************************/
void Send_data(uint8_t data)//参数为一个无符号8位的数如0x03
{
int i;
pin_out(C_CLR,LOW);//拉低清零位
pin_out(C_CLR,HIGH);//拉高清零位
for(i=0;i<8;i++){
pin_out(C_CLK,LOW);//时钟位置低
if((data&0x01)!=0)//对数据进行判断操作,控制数据引脚的输出
pin_out(C_A,HIGH);
else
pin_out(C_A,LOW);
data>>=1;
pin_out(C_CLK,HIGH);//时钟位置高,产生上升沿,发送1位数据
}
pin_out(C_CLK,LOW);//将时钟位置为低电平
}
5.定时器
*本文档只介详细绍定时器的基本使用方法,对匹配和捕获的原理只进行简单介绍
5.1 寄存器介绍
-
分频器寄存器(复位值为0)
- 预分频控制寄存器 PR :用于设定预分频值,为32位寄存器。
- 预分频计数器 PC :32位计数器,计数频率为PCLK,当计数值等于预分频计数器的值时,TC计数器加1。
- 定时器计数器 TC :32位计数器,计数频率为PCLK经过预分频计数器后频率值。
- 定时器计数时钟频率 = Fpclk / (PR+1)。Fpclk:24000000
-
匹配功能寄存器(复位值为0)
- 匹配控制寄存器 MCR :用于控制在匹配时是否产生中断或复位TC。
- 匹配寄存器 MRX(X为0~3):通过MCR寄存器可以设置匹配发生时的动作。
- 外部匹配寄存器 EMR :EMR控制外部匹配管脚MATx.0~MATx.3。
- 匹配控制寄存器 MCR : 0~2位为MR0部分(0:中断、1:复位、2:停止)。
-
捕获功能寄存器(复位值为0)
- 捕获控制寄存器 CCR :用于设置捕获信号的触发特征,以及捕获发生时是否产生中断。
- 捕获寄存器 CRX(X为0~3):在捕获X引脚上产生捕获时间时,CRX装载TC的值。
-
定时器使能&中断标志寄存器
-
定时器控制寄存器 TCR :控制定时器计数器的操作(第0位:计数器使能,1使能0停止;第1位:计数器复位)。
-
中断标志寄存器 IR:包含4个位用于匹配中断,另外4个位用于捕获中断(如下图)。
-
5.2 定时器初始化
-
初始化流程(匹配)
定时器编号X的取值为 0~3
设置TXTC的值 ==》设置TXPR的值 ==》初始化TXMCR ==》设置TXMR0的值 ==》对TXTCR进行赋值
-
示例
#define Fpclk 24000000 //本数值为XMUT开发板对应的值,具体情况示读者开发板类型而定
/*********************************初始化定时器 T0*******************************/
T0TC=0;//计数寄存器复位(清零)
T0PR=0;//设置预分频比为1
T0MCR=0x03;//设置匹配后产生中断和复位TC
T0MR0=Fpclk;//设定定时1S后的匹配值到MR0
T0TCR=0x01;//启动定时器0工作
NVIC_SetPriority(TIMER0_IRQn,4);//设置定时器中断优先级
NVIC_EnableIRQ(TIMER0_IRQn);//定时器中断使能
/*********************************定时器中断函数*********************************/
void TIMER0_IRQHandler (void){//对应定时器的函数名不能改变(TIMER‘X’ _IRQHandler)
T0IR=1;
//你自己的部分
}
6.UART串行通信
*十分适合用于程序的debug
6.1 发送&接收单元
- UART发送单元(X:0~1)
- 发送流程:CPU ==》UXTHR ==》UXTSR ==》TXD
- 发送器保持寄存器 UXTHR :写入该寄存器的值保存到发送FIFO中,当该字节到达FIFO底部时,它将被送入发送移位寄存器(UXTSR)进行发送。
- 发送FIFO缓冲区 :UART0、UART1各含有1个16字节的发送FIFO缓冲区,一直处于使能状态。
- UART接收单元 (X:0~1)
- 接收流程:RXD ==》UXRSR ==》UXRBR ==》CPU (X:0~1)
- 接收缓存寄存器UXRBR :包含了接收FIFO中最早接收到的字节。
- 接收FIFO缓冲区 :UART0、UART1各含有1个16字节的接收FIFO缓冲区,软件设置触发字节。
6.2 寄存器介绍
-
UART线状态寄存器----UXLSR (X:0~1)
线状态寄存器(UXLSR)为只读寄存器,它提供UARTX发送和接收模块的状态信息 。
-
UART波特率发生器
UART0和UART1各含有一个单独的波特率发生器,两者的功能相同,且相互独立。
这两个寄存器决定波特率时钟的频率,而波特率时钟必须是波特率的16倍。波特率计算公式如下:BaudRate = Fpclk/ ([U0DLM,U0DLL]×16)
-
UART中断使能寄存器-----UXIER(X:0~1)
UXIER可以控制UARTX的4个中断源。其中RBR中断使能包括两个中断,一个是接收数据可用(RDA)中断,一个是接收超时中断(CTI)。 -
中断标识寄存器-----UXIIR
UXIIR提供状态代码用于指示一个挂起中断的中断源和优先级。在访问UXIIR过程中,中断被冻结。如果在访问UXIIR时产生了中断,该中断将被记录,在下次访问UXIIR时可以读出,避免了中断的丢失。
6.3 UART中断
-
RLS中断
优先级最高,在UARTX发生下面的错误时产生中断,通过查看UXLSR[4:1]可以了解到产生该中断的错误条件。读取UXLSR时清除该中断。
- 溢出错误(OE)
- 奇偶错误(PE)
- 帧错误(FE)
- 间隔中断(BI)
-
RDA中断
与CTI中断并列为第二优先级,当接收的有效数据到达接收FIFO设置寄存器(UXFCR)中设置的触发点时,RDA被激活。当接收FIFO中的有效数据少于触发点时,RDA复位。
以下为产生中断的流程
- 移位寄存器(UXRSR)从RxDn引脚接收串行数据后,送入接收FIFO中。
- 当接收FIFO中的有效数据数量到达预定的触发点时,置位RDA中断。
- 从UXRBR寄存器中读取FIFO中最早到达的数据,当FIFO中的有效数据小于预定触发点时,清零RDA中断
-
CTI中断
当接收FIFO中的有效数据少于预定的触发点数量(至少有一个字节)时,如果在一定时间内仍然没有接收到新的数据,那将触发该中断。这个时间为:3.5~4.5个字节所需要的时间。注:对接收FIFO的任何操作都会清零该中断标志。
- 移位寄存器(UXRSR)从RxDn引脚接收串行数据后,送入接收FIFO中。
- 当接收FIFO中的有效数据少于触发个数,但至少有一个时,如果长时间没有数据到达,将触发CTI中断。
- 从UXRBR中读取接收FIFO中的数据,或者有新的数据送入接收FIFO,都将清零CTI中断。
-
THRE中断
该中断为第三优先级。当发送FIFO为空并且满足一定的条件时,该中断将被触发。
条件:
- 系统启动时,虽然发送FIFO为空,但不会产生THRE中断。
- 在上一次发生THRE中断后,向发送FIFO中写入1个字节数据,将在延时一个字节加上一个停止位后发生THRE中断。
- 如果在发送FIFO中有过两个字节以上的数据,但是现在发送FIFO为空时,将立即触发THRE中断。
6.4 UART初始化
-
操作流程
设置引脚连接模块将对应IO连接到UARTX ==》设置串口波特率 ==》设置串口工作模式 ==》发送或接收数据 ==》检查串口状态字或等待串口中断
-
示例(只展示RAD中断)
#define Fpclk 24000000
#define UART_BPS 9600
/*******************************初始化************************************/
uint16_t Fdiv;
PINSEL0=(PINSEL0 & ~(0x0F))|(0x05<<4);//配置P0.2、P0.3引脚为TXD0,RXD0
U0FCR=0x07;//使能FIFO功能 1字节触发
/*U0FCR的值:1字节触发:0x07、4字节触发0x41、8字节触发:0x81、14字节触发:0xc1*/
U0LCR=0x83;//使能波特率设置并设置锁帧格式
Fdiv=(Fpclk/16)/UART_BPS;
U0DLM=Fdiv/256;
U0DLL=Fdiv%256;//配置波特率为9600
U0LCR=0x03;//使能波特率值有效并设置锁帧格式
NVIC_SetPriority(UART0_IRQn, 2);//设置中断优先数
NVIC_EnableIRQ(UART0_IRQn);//中断使能
U0IER=0x01;//中断使能,使用RDA中断
/*******************************发送&接收************************************/
void UART0_SendByte(uint8_t data){//查询方式发送,参数为无符号8位数如0x01;char类型亦可
U0THR = data;
while((U0LSR&(1<<6))==0);
}
uint8_t UART0_RcvByte(void){//查询方式接收
uint8_t rcv_data;
while((U0LSR & 0x01) == 0);
rcv_data = U0RBR;
return(rcv_data);//返回一个无符号8位数,可视为一个char类型
}
void UART0_IRQHandler(void){//中断方式查询接收(函数名不可修改)
char c=UART0_RcvByte();
}
/*采用中断查询可以避免程序一直在等待数据传入*/
7.SPI串行通信
*SPI可简化对一些电子器件的使用
7.1 引脚介绍
引脚名称 | 类型 | 描述 |
---|---|---|
SCK | I/O | 串行时钟。用于同步SPI接口间数据传输的时钟信号。该时钟信号总是由主机输出。 |
SSEL | I | 从机选择。SPI从机选择信号是一个低有效信号。 |
MISO | I/O | 主入从出。MISO信号是一个单向的信号,它将数据由从机传输到主机。 |
MOSI | I/O | 主出从入。MOSI信号是一个单向的信号,它将数据从主机传输到从机。 |
7.2 寄存器介绍

- SPI控制寄存器——SPCR
位 | 7 | 6 | 5 | 4 | 3 | 2 : 0 |
---|---|---|---|---|---|---|
功能 | SPIE | LSBF | MSTR | CPOL | CPHA | 保留 |
-
CPHA:时钟相位控制(复位值 :0)
该位决定SPI传输时数据和时钟的关系,并控制从机传输的起始和结束
1 : 时钟前沿数据输出,后沿数据采样;
0 : 时钟前沿数据采样,后沿数据输出;
-
CPOL:时钟极性控制(复位值:0)
1 : SCK为低电平有效;
0 : SCK为高电平有效;
-
MSTR:主模式控制(复位值:0)
1 : SPI处于主模式;
0 : SPI处于从模式
-
LSBF:字节移动方向控制(复位值:0)
1 : 每字节数据从低位(LSB)开始传输;
0 : 每字节数据从高位(MSB)开始传输;
-
SPIE:SPI中断使能(复位值:0)
1 : 每次SPIF或MODF置位时都会产生硬件中断;
0 : SPI中断被禁止;
-
SPI状态寄存器——SPSR
SPSR寄存器为只读寄存器,用于监视SPI功能模块的状态,包括一般性功能和异常状况。 -
SPI数据寄存器——SPDR
SPDR | 功能 | 描述 | 复位值 |
---|---|---|---|
7 : 0 | 数据 | SPI双向数据 | 0 |
SPDR寄存器为SPI提供数据的发送和接收。
处于主模式时,向该寄存器写入数据,将启动SPI数据传输。从数据传输开始到SPIF状态位置位并且没有读取状 态寄存器的这段时间内不能对该寄存器执行写操作。
- SPI时钟计数寄存器——SPCCR
SPCCR | 功能 | 描述 | 复位值 |
---|---|---|---|
7 : 0 | 计数值 | 设定SPI时钟计数值 | 0 |
SPI速率 = Fpclk / SPCCR
注意:作为主机时,SPCCR寄存器控制SCK的频率。寄存器的值为一位SCK时钟所占用的PCLK周期数。该寄存器 的 值必 须为偶数,并且必须不小于8。
- SPI中断寄存器——SPINT
7.3 应用示例
/**********************************初始化——作为主机*********************************/
#define MSTR (1<<5)
#define CPOL (1<<4)
#define CPHA (1<<3)
#define LSBF (1<<6)
#define SPI_MODE (MSTR|CPOL)
#define Fpclk 24000000
void MSpiIni(uint8_t fdiv){//设置SCK并使能
if(fdiv<8)//判断数据的合法性
fdiv=8;
S0SPCCR=fdiv&0xFE;//配置SPI的SCK时钟
S0SPCR=SPI_MODE;//设置SPI为主机模式,并使能SPI
}
void SPI_Init(void){//初始化函数
PINSEL0=(PINSEL0 &~(0x03<<30))|(0x03<<30);//配置0.15位SCK功能引脚
PINSEL1=(PINSEL1 &~(0x0F<<2))|(0x0F<<2);//P0.17和P0.18为MISO和MOSI功能
MSpiIni(0x56);//使用上面的设置&使能函数
}
/*************************************发送数据**************************************/
uint8_t MSendData(uint8_t data){//SPI发送数据函数,参数为无符号8位数
S0SPDR=data;//向SPI写数据
while(0==(S0SPSR&0x80));//等待SPI发送完毕
return data;//返回参数数据
}
/*本示例暂不提供作为从机的代码*/
8. 74HC95芯片
8.1 接线图
*本图来源CCP

8.2 应用示例
/*忽略SPI初始化内容*/
uint8_t MSendData(uint8_t data){//SPI发送数据函数驱动74HC95,参数为无符号8位数
S0SPDR=data;//向SPI写数据
while(0==(S0SPSR&0x80));//等待SPI发送完毕
set_pin_out(ST_CP,HIGH);
set_pin_out(ST_CP,LOW);//使用SPI传输数据时要将ST_CP连接的引脚拉高拉低来刷新内容
return data;//返回参数数据
}
9.LCD1602显示
9.1 引脚&功能
编号 | 符号 | 引脚说明 | 编号 | 符号 | 引脚说明 |
---|---|---|---|---|---|
1 | VSS | 电源地 | 9 | D2 | 数据 |
2 | VDD | 电源正极(5V) | 10 | D3 | 数据 |
3 | VL | 液晶显示偏压 | 11 | D4 | 数据 |
4 | RS | 数据/命令选择 | 12 | D5 | 数据 |
5 | R/W | 读/写选择 | 13 | D6 | 数据 |
6 | E | 使能信号 | 14 | D7 | 数据 |
7 | D0 | 数据 | 15 | BLA | 背光源正极 |
8 | D1 | 数据 | 16 | BLK | 背光源负极 |
- 部分引脚说明
- 第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。
- 第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
- 第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。
- 第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
9.2 指令说明及时序
- 指令表
序号 | 指令 | RS | R/W | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
1 | 清除显示 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
2 | 光标返回 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | * |
3 | 置为输入模式 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S |
4 | 显示开关控制 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B |
5 | 光标或字符移位 | 0 | 0 | 0 | 0 | 0 | 1 | S/C | R/L | * | * |
6 | 置功能 | 0 | 0 | 0 | 0 | 1 | DL | N | F | * | * |
7 | 置字符发生存贮器地址 | 0 | 0 | 0 | 1 | 字符发生存贮器地址 | |||||
8 | 置数据存贮器地址 | 0 | 0 | 1 | 显示数据存贮器地址 | ||||||
9 | 置数据存贮器地址 | 0 | 1 | BF | 计数器地址 | ||||||
10 | 写数到CGRAM或DDRAM | 1 | 0 | 要写的数据内容 | |||||||
11 | 从CGRAM或DDRAM读数 | 1 | 1 | 读出的数据内容 |
- 时序图

9.3 地址映射
- 地址映射图
9.4 初始化&显示
- 示例
/*************************************初始化部分*************************************/
void lcd_Wcmd(uint8_t data){//发送指令函数
myDelay(15);//延时15ms
pin_out(RS,LOW);//将连接RS的引脚设为低电平,对应指令模式
pin_out(RW,LOW);//将RW设为低电平,对应输入模式
pin_out(E,HIGH);//将E端设为高电平
MSendData(data);//使用SPI发送8位数据
pin_out(E,LOW);//将E端设置为低电平,LCD执行指令
}
void lcd_Wdata(uint8_t data){//发送数据函数
myDelay(15);//延时15ms
pin_out(RS,HIGH);//将连接RS的引脚设为高电平,对应数据模式
pin_out(RW,LOW);//将RW设为低电平,对应输入模式
pin_out(E,HIGH);//将E端设为高电平
MSendData(data);//使用SPI发送8位数据
pin_out(E,LOW);//将E端设置为低电平
}
void LCD_Init(void){//LCD初始化函数
lcd_Wcmd(0x38);//功能设置
myDelay(1);//延时1ms
lcd_Wcmd(0x0C);//显示设置
myDelay(1);
lcd_Wcmd(0x06);//输入方式从左到右
myDelay(1);
lcd_Wcmd(0x01);//清屏
myDelay(1);
}
/**************************************基础显示部************************************/
void lcd_pos(uint8_t pos){//切换光标位置函数
lcd_Wcmd(pos|0x80);
}
void LCD_print(uint8_t pos,uint8_t c){//显示字符,参数pos:显示的位置,c:字符
lcd_pos(pos);//移动光标
lcd_Wdata(c);//输出字符
}
LCD_print(0x00,'a');//在第一行第一个字符的位置打印字符‘a’
/*显示字符可以直接在设置光标位置之后输入数据显示字符*/
9.5 自定义字符
-
显示汉字参考
-
自定义字符
- LCD1602字符位为5*8的点阵。
- 字符格式:一个自定义字符由8个5位无字符数过程(例如:0x00,0x04,0x15,0x0E,0x1F,0x0E,0x11,0x00)。
- 字符示例:无符号5位数0x1E(11110)显示一行的前4个像素点。
-
示例
font_map[]={0x00,0x04,0x15,0x0E,0x1F,0x0E,0x11,0x00};//自定义字符示例
/*将自定义字符存入CGRAM*/
void flesh_RAM(uint8_t font_map[],int line){//刷新CGRAM,将字符值存入
lcd_Wcmd(0x40);//输入指令,准备写入
for(int i=0;i<line*8;i++){//line:要写入的字符数
lcd_Wdata(font_map[i]);//写入字符数据,front_map:存放自定义字符
myDelay(5);//延时5ms
}//用户可以使用二维数组存放字符
}
/*显示自定义字符*/
LCD_print(0x40,0x00);//在第二行第一个字符的位置显示第一个自定义字符
/*自定义字符的编号(1~8)为:0x00、0x01、0x02、0x03、0x04、0x05、0x06、0x07*/
10.矩阵键盘
10.1 扫描策略
-
扫描法
通过高四位轮流输出低电平来对矩阵键盘进行逐行扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,然后通过接收到的数据是哪一位为0来判断是哪一个按键被按下。
-
行列反转法
通过高四位全部输出低电平,低四位设为输入模式。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下,然后再反过来,高四位设置为输入模式,低四位输出低电平,然后根据接收到的高四位的值判断是那一行有按键按下,这样就能够确定是哪一个按键按下了。
10.2 示例
*该示例使用方法并非最佳方案
float key_up[4];//上4行引脚值(行)
float key_down[4];//下4行引脚值(列)
char switch_key(int num){//判断按键按下的值
switch(num){
case 11:return '1';
case 21:return '2';
case 31:return '3';
case 12:return '4';
case 22:return '5';
case 32:return '6';
case 13:return '7';
case 23:return '8';
case 33:return '9';
case 41:return 'a';
case 42:return 'b';
case 43:return 'c';
case 44:return 'd';
case 14:return '*';
case 24:return '0';
case 34:return '#';
}
return 0;
}
/***************************************扫描法***********************************/
void set_low(int n,float key_up[]){
for(int i=0;i<4;i++){
set_pin_out(key_up[i],HIGH);
}
set_pin_out(key_up[n],LOW);
}
char keyBoard(float key_up[],float key_down[]){
myDelay(350);//消抖,延时350ms,用户可根据实际情况修改
for(int i=0;i<4;i++){
set_low(i,key_up);//设置其中一行输出低电平
for(int j=0;j<4;j++){
set_pin(key_down[j],INPUT);//设置其中一列为输入
/*注意:可在引脚初始化时设为输入,写在此处是为了减轻代码量*/
if(judge_pin(key_down[j])==0){//判断引脚是否为低电平
myDelay(10);//延时10ms
char c=switch_key((i+1)+(j+1)*10);//对结果进行处理
//UART0_SendByte(c);可使用UART串口检查
return c;//返回按键处理的结果值
}
}
}
return 0;//无按键按下默认返回0即‘\0’
}
/*************************************行列反转法***********************************/
char keyBoard(float key_up[],float key_down[]){
myDelay(400);//消抖,延时400ms,用户可根据实际情况修改
int up=-1,down=-1;//记录上4个引脚和下4个引脚的值
for(int i=0;i<4;i++){
set_pin_out(key_up[i],LOW);//上4个引脚输出低电平
}
for(int i=0;i<4;i++){
set_pin(key_down[i],INPUT);//下4个设置为低电平
}
for(int i=0;i<4;i++){
if(judge_pin(key_down[i])==0){
myDelay(20);//消抖
if(judge_pin(key_down[i])==0){
up=i+1;//记录引脚值
break;
}
}
}
myDelay(20);//延时20ms,以下为反转部分
for(int i=0;i<4;i++){
set_pin_out(key_down[i],LOW);//下4个引脚输出低电平
}
for(int i=0;i<4;i++){
set_pin(key_up[i],INPUT);//上4个引脚设置为输入模式
}
for(int i=0;i<4;i++){
if(judge_pin(key_up[i])==0){
myDelay(20);//消抖
if(judge_pin(key_up[i])==0){
down=i+1;//记录引脚值
break;
}
}
}
if(down>0&&up>0){//判断记录值是否有效
char n=switch_key(up*10+down);//处理结果,得到按键的值
//UART0_SendByte(n);可使用UART串口检查
return n;//返回按键的值
}
return 0;
}
11.RTC时钟模块
11.1 寄存器介绍
时钟产生寄存器组
名称 | 有效位 | 描述 | 访问 |
---|---|---|---|
PREINT | 13 | 预分频值,整数部分 | 读写 |
PREFRAC | 15 | 预分频值,小数部分 | 读写 |
CCR | 4 | 时钟控制寄存器 | 读写 |
CTC | 15 | 时钟节拍计数器 | 只读 |
- 预分频寄存器——PREINT、PREFRAC
预分频整数部分为13位有效位,小数部分为15位有效位
预分频整数部分的计算公式为:PREINT = int(PCLK / 32768) – 1
预分频小数部分的计算公式为:PREFRAC = PCLK – ((PREINT + 1) × 32768)
-
时钟控制寄存器——CCR
时钟控制寄存器包含4位有效位,用来对时钟分频电路进行控制,包括启动RTC和复位时钟节拍计数器(CTC)等功能。
-
时钟节拍计数寄存器——CTC
时钟节拍计数器对预分频器的输出时钟进行计数,用于产生秒的时钟节拍。它是一个只读寄存器,但它可通过时钟控制寄存器(CCR)复位为0。
时间寄存器组
- 完整时间寄存器
名称 | 有效位 | 描述 |
---|---|---|
CTIME0 | 32 | 包含秒、分、时和星期 |
CTIME1 | 32 | 包含日期(月)、月和年 |
CTIME2 | 32 | 包含日期(年) |
- CTIME0
CTIME0 | 31:27 | 26:24 | 23:21 | 30:16 | 15:14 | 13:8 | 7:6 | 5:0 |
---|---|---|---|---|---|---|---|---|
功能 | 保留 | 星期 | 保留 | 小时 | 保留 | 分 | 保留 | 秒 |
取值范围 | — | (0~6) | — | (0~23) | — | (0~59) | — | (0~59) |
- CTIME1
CTIME1 | 31:28 | 27:16 | 15:12 | 11:8 | 7:5 | 4:0 |
---|---|---|---|---|---|---|
功能 | 保留 | 年 | 保留 | 月 | 保留 | 日期(月) |
选取范围 | — | (0~4095) | — | (1~12) | — | (1~28,29,30) |
- CTIME2
CTIME2 | 31:9 | 8:0 |
---|---|---|
功能 | 保留 | 日期(年) |
取值范围 | — | (1~365,366) |
注意:CTIME1与CTIME2这两个寄存器意义不同,(月)表示当日在当月中的序号,(年)表示当日在当年中的序号
- 分类时间寄存器
名称 | 有效位 | 描述 |
---|---|---|
SEC | 6 | 秒值。该值的范围为0~59。 |
MIN | 6 | 分值。该值的范围为0~59。 |
HOUR | 5 | 小时值。该值的范围为0~23。 |
DOM | 5 | 日期(月)值。该值的范围为1~28,29,30或31(取决于月份以及是否为闰年)。 |
DOW | 3 | 星期值。该值的范围为1~365(闰年为366)。 |
DOY | 9 | 日期(年)值。该值的范围为1~365。 |
注意:
这些日期的寄存器只能在适当的时间间隔处递增,而在定义的溢出点处复位。为了使这些值有意义,它们不能进行计算且必须正确初始化。
其中DOY寄存器需要单独初始化,也就是说该寄存器的值不会因为对年、月、日寄存器进行初始化而自动确定到一个正确的值。
中断产生控制寄存器
- 中断产生寄存器
名称 | 描述 | 访问 |
---|---|---|
ILR | 中断位置寄存器 | 读写 |
CIIR | 递增中断寄存器 | 读写 |
AMR | 报警屏蔽寄存器 | 读写 |
报警寄存器组 | 设定报警时间 | 读写 |
RTC中断分为两类:
-
时间计数器的增量中断,由增量中断寄存器控制。
-
报警匹配产生的中断,由报警屏蔽寄存器控制。
-
中断标志寄存器——ILR
-
增量中断使能寄存器——CIIR
增量中断寄存器可使计数器每次增加时产生一次中断
-
报警屏蔽寄存器——AMR
报警屏蔽寄存器允许用户屏蔽任意的报警寄存器,被屏蔽的报警寄存器将不与时间计数器比较。
未被屏蔽的报警寄存器与时间计数器比较如果匹配,将产生中断。该中断只有在从不匹配到匹配时才发生,可以避免中断重复。
注意:如果所有屏蔽位都置位,报警将被禁止。
11.2 应用示例
- 示例
typedef struct TIME{//定义时间数据结构体
uint8_t sec;
uint8_t min;
uint8_t hour;
uint8_t day;
uint8_t mon;
uint8_t week;
uint16_t year;
}time_data;
/******************************初始化RTC模块*****************************/
void set_time_now(time_data *time){//初始化分类时间寄存器中的值
SEC=time->sec;//秒
MIN=time->min;//分
HOUR=time->hour;//时
DOM=time->day;//日
MONTH=time->mon;//月
DOW=time->week;//星期
YEAR=time->year;//年
}
void set_time_alarm(time_data *time){//设置时间报警中断的值
ALSEC=time->sec;//秒
ALMIN=time->min;//分
ALHOUR=time->hour;//时
ALDOM=time->day;//日
ALMONTH=time->mon;//月
ALDOW=time->week;//星期
ALYEAR=time->year;//年
}
void RTC_Init(time_data *time){
CCR=0x00;//禁止时间计数器,以便对其初始化
ILR=0x03;//清除两个中断标志
CIIR=0x01;//开启秒增值中断
AMR=0xFF;//关闭报警中断,值为0xF8开启时分秒报警中断
set_time_now(time);//初始化时间值
NVIC_SetPriority(RTC_IRQn,4);//设置RTC中断优先级
NVIC_EnableIRQ(RTC_IRQn);//中断使能
CCR=0x01;//启动定时器
}
/******************************RTC中断*****************************/
void RTC_IRQHandler(void){//RTC中断
if((ILR&0x02)!=0){//报警中断
ILR |=0x02;//清除中断标志
//你自己的部分
}
if((ILR&0x01)!=0){//增量中断
ILR |=0x01;//清除中断标志
//你自己的部分
}
}
12.EZ1768.h
*本部分仅供参考
- 源代码
#include "LPC17xx.h"
#include "LPC1768.h"
#define PULLUP 4 //上拉模式
#define HIGH 1 //高电平
#define LOW 0 //低电平
#define OUTPUT 1 //输出
#define INPUT 0 //输入
#define RISING 2 //上升沿
#define FALLING 3 //下降沿
int DL_MOD[][8]={
{0,0,0,0,0,0,1,1},//0
{1,0,0,1,1,1,1,1},//1
{0,0,1,0,0,1,0,1},//2
{0,0,0,0,1,1,0,1},//3
{1,0,0,1,1,0,0,1},//4
{0,1,0,0,1,0,0,1},//5
{0,1,0,0,0,0,0,1},//6
{0,0,0,1,1,1,1,1},//7
{0,0,0,0,0,0,0,1},//8
{0,0,0,0,1,0,0,1},//9
{0,0,0,1,0,1,0,1},//A(10)
{1,1,0,0,0,0,0,1},//b(11)
{0,1,1,0,0,0,1,1},//c(12)
{1,0,0,0,0,1,0,1},//d(13)
{0,1,1,0,0,0,0,1},//E(14)
{0,1,1,1,0,0,0,1},//F(15)
{1,1,1,0,0,0,1,1},//L(16)
{1,1,1,1,1,1,0,1},//-
{1,1,1,0,1,1,1,1},//下划线
};
float digital_com[12];//存放数码管引脚
volatile unsigned long *select_pin(int i){//返回PINSEL寄存器的地址
switch(i){
case 0:return &PINSEL0;
case 1:return &PINSEL1;
case 2:return &PINSEL2;
case 3:return &PINSEL3;
case 4:return &PINSEL4;
case 7:return &PINSEL7;
case 8:return &PINSEL8;
case 9:return &PINSEL9;
case 10:return &PINSEL10;
}
return 0;
}
volatile unsigned long * select_mode(int i){//返回PINMODE寄存器的地址
switch(i){
case 0:return &PINMODE0;
case 1:return &PINMODE1;
case 2:return &PINMODE2;
case 3:return &PINMODE3;
case 4:return &PINMODE4;
case 5:return &PINMODE5;
case 6:return &PINMODE6;
case 7:return &PINMODE7;
case 9:return &PINMODE9;
}
return 0;
}
volatile unsigned long * fio_dir(int i){//返回FIODIR寄存器的地址
switch(i){
case 0:return &FIO0DIR;
case 1:return &FIO1DIR;
case 2:return &FIO2DIR;
case 3:return &FIO3DIR;
case 4:return &FIO4DIR;
}
return 0;
}
volatile unsigned long * fio_set(int i){//返回FIOSET寄存器的地址
switch(i){
case 0:return &FIO0SET;
case 1:return &FIO1SET;
case 2:return &FIO2SET;
case 3:return &FIO3SET;
case 4:return &FIO4SET;
}
return 0;
}
volatile unsigned long *fio_clr(int i){返回FIOCLR寄存器的地址
switch(i){
case 0:return &FIO0CLR;
case 1:return &FIO1CLR;
case 2:return &FIO2CLR;
case 3:return &FIO3CLR;
case 4:return &FIO4CLR;
}
return 0;
}
volatile unsigned long * fio_pin(int i){//返回FIOPIN寄存器的地址
switch(i){
case 0:return &FIO0PIN;
case 1:return &FIO1PIN;
case 2:return &FIO2PIN;
case 3:return &FIO3PIN;
case 4:return &FIO4PIN;
}
return 0;
}
void set_inter(int i){//设置外部中断引脚
switch(i){
case 0:PINSEL4 |=(0x01<<20);break;
case 1:PINSEL4 |=(0x01<<22);break;
case 2:PINSEL4 |=(0x01<<24);break;
case 3:PINSEL4 |=(0x01<<26);break;
}
}
void set_inter_mod(int mod,int i){//设置外部中断模式
switch(mod){
case 0:{EXTMODE &=(~(0x01<<i));EXTPOLAR &=(~(0x01<<i));break;}
case 1:{EXTMODE &=(~(0x00<<i));EXTPOLAR |=(0x01<<i);break;}
case 2:{EXTMODE |=(0x01<<i);EXTPOLAR |=(0x01<<i);break;}
case 3:{EXTMODE |=(0x01<<i);EXTPOLAR &=(~(0x01<<i));break;}
}
}
IRQn_Type EINTx_IRQn(int i){//返回中断使能函数使用的参数
switch(i){
case 0:return EINT0_IRQn;
case 1:return EINT1_IRQn;
case 2:return EINT2_IRQn;
case 3:return EINT3_IRQn;
}
return EINT0_IRQn;
}
int order_pin(float n,int *num){//处理float类型的引脚名,如0.01:表示P0.1引脚
int i;
i=(int)n;
*num=((int)(n*100))%100;
return i;
}
int judge_pin(float pin){//返回引脚的状态
int fn,bn;
fn=order_pin(pin,&bn);
return (*fio_pin(fn)&(1ul<<bn));
}
int set_GPIO(float pin){//设置GPIO
int fn,bn;
fn=order_pin(pin,&bn);
*select_pin(bn<16?fn:fn+1)&= ~(0x3 << bn<16?bn:bn-16);
return 0;
}
int set_pin(float pin,int c){//设置引脚
int fn,bn;
set_GPIO(pin);
fn=order_pin(pin,&bn);
if(c==1)//设置为输出
*fio_dir(fn)|=(1ul<<bn);
else if(c==0)//设置为输入
*fio_dir(fn)&=~(1ul<<bn);
return 0;
}
int pin_out(float pin,int i){//设置输出的电平*需要在设置为输出引脚后使用
int fn,bn;
fn=order_pin(pin,&bn);
if(i==1){//输出高电平
*fio_set(fn)=(1ul<<bn);
}
else if(i==0){//输出低电平
*fio_clr(fn)=(1ul<<bn);
}
return 0;
}
int set_pin_out(float pin,int c){//设置引脚为输出,并设置输出的电平
set_GPIO(pin);
set_pin(pin,OUTPUT);
pin_out(pin,c);
return 1;
}
void set_interrupt(int com,int mod,int level){//设置外部中断
/*com:中断的序号、mod:中断的模式、level:中断的优先数*/
set_inter(com);
set_inter_mod(mod,com);
EXTINT =0x0F;
NVIC_SetPriority(EINTx_IRQn(com),level);
NVIC_EnableIRQ(EINTx_IRQn(com));
}
void set_Dlight_com(float n1,float n2,float n3,float n4,float A,float B,float C,float D,float E,float F,float G,float pd){//初始化数码管引脚
digital_com[0]=n1;
digital_com[1]=n2;
digital_com[2]=n3;
digital_com[3]=n4;
digital_com[4]=A;
digital_com[5]=B;
digital_com[6]=C;
digital_com[7]=D;
digital_com[8]=E;
digital_com[9]=F;
digital_com[10]=G;
digital_com[11]=pd;
}
void select_com(int n){//切换数码管引脚
int i;
for(i=0;i<4;i++){
pin_out(digital_com[i],LOW);
}
switch(n){
case 0:pin_out(digital_com[0],HIGH);break;
case 1:pin_out(digital_com[1],HIGH);break;
case 2:pin_out(digital_com[2],HIGH);break;
case 3:pin_out(digital_com[3],HIGH);break;
default:break;
}
}
void reset_D(void)//去除余晖
{
int i;
for(i=4;i<12;i++)
{
pin_out(digital_com[i],HIGH);
}
}
void print_Dnum(int pin,int n)//显示单个数字
{
int i;
int j=0;
select_com(pin);
reset_D();
for(i=4;i<12;i++){
pin_out(digital_com[i],DL_MOD[n][j++]);
}
}
void print_digital_num(int n){//显示多个数字
int result[10]={0};
int temp=n,i=0,j,flag=0;
int result_l=0;
if(n<0)temp=-temp;
result_l=0;
while(temp){
result[result_l++]=temp%10;
temp=temp/10;
}
if(n<0){
result[result_l++]=16;
}
if(result_l<4)result_l=4;
if(flag==0)temp=result_l;
for(j=temp-1;j>=0;j--){
reset_D();
print_Dnum(i++,result[j]);
}
flag++;
if(flag%5000==0)temp--;
else if(temp<4)flag=0;
}