感谢北京化工大学何宾老师的课程指导,今天我们分享一个利用STC15W4K32S4单片机实现的ADC应用,我们通过ADC转换实现在LCD1602上显示学号,并能够通过外部中断0和外部中断1分别实现学号左移和右移的功能。
以下是项目代码:
1、lcd1602.h
// 条件编译指令,如果未定义_1602_则定义_1602
#ifndef _1602_
#define _1602_
// 引入头文件
#include "reg51.h"
#include "intrins.h"
// 定义LCD引脚及寄存器地址
sbit LCD1602_RS = P2^5; // 定义LCD1602_RS为P2.5引脚,RS=0表示传输的是控制指令,RS=1表示传输的是数据
sbit LCD1602_RW = P2^6; // 定义LCD1602_RW为P2.6引脚,R/W=0表示读操作,R/W=1表示写操作
sbit LCD1602_E = P2^7; // 定义LCD1602_E 为P2.7引脚,E=1,单片机将数据放入数据线,一段时间后拉低,数据继续维持一段时间,即写入LCD中
sfr LCD1602_DB = 0x80; // 定义LCD1602_DB为P0端口
sfr P0M1 = 0x93; // 定义P0端口P0M1寄存器地址0x93
sfr P0M0 = 0x94; // 定义P0端口P0M0寄存器地址0x94
sfr P2M1 = 0x95; // 定义P2端口P2M1寄存器地址0x95
sfr P2M0 = 0x96; // 定义P2端口P2M0寄存器地址0x96
// 申明函数
void lcdwait(); // 等待LCD空闲函数
void lcdwritecmd(unsigned char cmd); // 写LCD命令函数
void lcdwritedata(unsigned char dat); // 写LCD数据函数
void lcdinit(); // 初始化LCD函数
void lcdsetcursor(unsigned char x, unsigned char y); // 设定LCD光标位置函数
void lcdshowstr(unsigned char x, unsigned char y, unsigned char *str); // 打印字符串函数
// 条件预编译指令结束
#endif
2、led1602.c
// 引入头文件
#include "led1602.h"
// 实现函数
/**
* 函数名:lcdwait()
* 参数 :无
* 功能 :等待LCD空闲
*/
void lcdwait() {
LCD1602_DB = 0xFF; // 读取前,置P0端口为FF,引脚相当于悬空
_nop_(); // 空操作指令,延迟
_nop_();
_nop_();
_nop_();
LCD1602_RS = 0; // 将RS信号拉低
LCD1602_RW = 1; // 将RW信号拉高
LCD1602_E = 1; // 将E 信号拉高
while(LCD1602_DB & 0x80); // 等待标志BF为低,即LCD空闲
LCD1602_E = 0; // 将E 信号拉低
}
/**
* 函数名:lcdwritecmd()
* 参数 :cmd 控制指令码
* 功能 :写LCD命令
*/
void lcdwritecmd(unsigned char cmd) {
lcdwait(); // 等待LCD空闲
_nop_();
_nop_();
_nop_();
_nop_();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd; // 将控制指令码放到P0端口
LCD1602_E = 1;
_nop_();
_nop_();
_nop_();
_nop_();
LCD1602_E = 0;
}
/**
* 函数名:lcdwritecmd()
* 参数 :dat 数据码
* 功能 :写LCD数据
*/
void lcdwritedata(unsigned char dat) {
lcdwait(); // 等待LCD空闲
_nop_();
_nop_();
_nop_();
_nop_();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat; // 将数据码放到P0端口
LCD1602_E = 1;
_nop_();
_nop_();
_nop_();
_nop_();
LCD1602_E = 0;
}
/**
* 函数名:lcdinit()
* 参数 :无
* 功能 :初始化LCD
*/
void lcdinit() {
lcdwritecmd(0x38); // 发指令0x38,2行模式,5x8点阵,8位宽度
lcdwritecmd(0x0c); // 发指令0x0c,打开显示,关闭光标
lcdwritecmd(0x06); // 发指令0x06,文字不移动,地址自动加1
lcdwritecmd(0x01); // 发指令0x01,清屏
}
/**
* 函数名:lcdsetcursor()
* 参数 :x 在LCD上的列数
* y 在LCD上的行数
* 功能 :设定LCD光标位置
*/
void lcdsetcursor(unsigned char x, unsigned char y) {
unsigned char address; // 存储器地址
if(y==0) // 在第一行
address = 0x00 + x;
else // 在第二行
address = 0x40 + x;
lcdwritecmd(address | 0x80); // 写存储器地址指令
}
/**
* 函数名:lcdshowstr()
* 参数 :x 在LCD上的列数
* y 在LCD上的行数
* str 需要打印的字符串
* 功能 :打印字符串
*/
void lcdshowstr(unsigned char x, unsigned char y, unsigned char *str) {
lcdsetcursor(x, y); // 设置光标位置
while((*str) != '\0') { // 字符串未到结尾
lcdwritedata(*str); // 传送字符数据
str++; // 字符串地址自增
}
}
3、main.c
// 引入头文件
#include "reg51.h"
#include "stdio.h"
#include "led1602.h"
#define ADC_POWER 0x80 // 定义ADC_POWER 的值为0x80
#define ADC_FLAG 0x10 // 定义ADC_FLAG 的值为0x10
#define ADC_START 0x08 // 定义ADC_START 的值为0x08
#define ADC_SPEEDLL 0x00 // 定义ADC_SPEEDLL的值为0x00
#define ADC_SPEEDL 0x20 // 定义ADC_SPEEDL 的值为0x20
#define ADC_SPEEDH 0x40 // 定义ADC_SPEEDH 的值为0x40
#define ADC_SPEEDHH 0x60 // 定义ADC_SPEEDHH的值为0x60
sfr AUXR = 0x8E; // 申明AXUR寄存器的地址为0x8E
sfr ADC_CONTR = 0xBC; // 申明ADC_CONTR控制寄存器的地址为0xBC
sfr ADC_RES = 0xBD; // 申明ADC_RES结果寄存器的地址为0xBD
sfr ADC_RESL = 0xBE; // 申明ADC_RESL结果寄存器的地址为0xBE
sfr P1ASF = 0x9D; // 申明P1ASF模拟功能控制寄存器的地址为0x9D
// 在外部数据区定义变量,用于存储学号
xdata long num1 _at_ 0x100; // 定义长整形变量num1
xdata float num2 _at_ 0x120; // 定义浮点数变量num2
unsigned char ch = 4;
bit flag = 1; // ADC转换完成标识
unsigned char tstr[50]; // 数据存储缓冲器
/**
* 函数名:delay
* 参数 :n 延迟时间长度,单位毫秒
* 功能 :延时指定的毫秒时间
*/
void delay(unsigned int n) {
unsigned int i, j;
for(i = n; i > 0; i--) {
for(j = 114; j > 0; j--);
}
}
/**
* 函数名:adc_int
* 参数 :无
* 功能 :ADC中断响应函数
*/
void adc_int() interrupt 5 {
unsigned char i = 0;
ADC_CONTR &= !ADC_FLAG; // 将ADC_FLAG标志清零
num1 = 2016014446;
num2 = 2016014446;
sprintf(tstr + 10, "%lld", num1); // 将数据传送至缓冲区
flag = 1; // ADC转换完成
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch; // 启动ADC
}
/**
* 函数名:move_left
* 参数 :无
* 功能 :外部中断0中断响应函数,实现学号在LCD上左移
*/
void move_left() interrupt 0 {
unsigned char i;
for(i = 0; i < 11; i++){
lcdwritecmd(0x01); // 清屏
lcdwait(); // 等待LCD空闲
lcdshowstr(0, 0, tstr+10+i); // 右移数组地址,写字符串
lcdwait();
delay(3000); // 延时等待
}
}
/**
* 函数名:move_right
* 参数 :无
* 功能 :外部中断1中断响应函数,实现学号在LCD上右移
*/
void move_right() interrupt 2 {
unsigned char i;
for(i = 6; i < 17; i++){
lcdwritecmd(0x01); // 清屏
lcdwait(); // 等待LCD空闲
lcdshowstr(i, 0, tstr+10); // 右移光标位置,写字符串
lcdwait();
delay(3000); // 延时等待
}
}
/**
* 函数名:main
* 参数 :无
* 功能 :主函数,在LCD上打印学号,响应中断
*/
void main() {
unsigned int i, k;
P0M0 = 0; // 通过P0M0和P0M1寄存器将P0口定义为准双向(双向IO),弱上拉(上拉电阻很大,提供的驱动电流很小,叫弱上拉)
P0M1 = 0;
P2M0 = 0; // 通过P2M0和P2M1寄存器将P2口定义为准双向,弱上拉
P2M1 = 0;
P1ASF = 0xFF; // 将P1端口用于ADC输入
ADC_RES = 0; // 将ADC_RES寄存器清零
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch; // 配置ADC_CONTR寄存器
for(i = 0; i < 10000; i++);
IE = 0xA0; // 允许ADC中断
lcdwait(); // 等待LCD空闲
lcdinit(); // 初始化LCD
IT0 = 1; // 开启外部中断0和外部中断1
EX0 = 1;
IT1 = 1;
EX1 = 1;
EA = 1;
while(1) {
if(flag == 1) {
flag = 0;
for(k = 0; k < 16; k++) {// 循环打印学号
lcdwritecmd(0x01);
lcdwait();
lcdshowstr(k, 0, tstr+10);
if(k > 6)
lcdshowstr(0, 0, tstr+26-k);
lcdwait();
delay(500);
}
}
}
}
有关讲解的视频欢迎关注我们的公众号:落饼枫林,查看历史推送:STC15W4K32S4单片机ADC应用实现。
想要学习更多单片机的知识也欢迎参加何老师在爱课程网站上的单片机原理及应用的课程:https://www.icourse163.org/learn/BUCT-1205804839#/learn/announce