1. 引言
因新公司之前液晶数显表产品单片机一直用的是 C51单片机(SH79F9202U9),本人之前没有接触过这款单片机,为了维护老产品不得不重新研究研究这款单片机。
10位ADC + LCD的增强型8051微控制器
SH79F9202是一种高速高效率8051可兼容单片机。在同样振荡频率下,较之传统的8051芯片它有着运行更快速的优越特性。
SH79F9202保留了标准8051芯片的大部分特性。这些特性包括内置256字节RAM,2个UART,外部中断0/2/3和外部中断4(8路输入)。此外,SH79F9202还集成了外部1280字节RAM,以及程序存储的最大32K字节Flash,此外还集成了最大4K类EEPROM,用于在系统掉电后保存数据。
SH79F9202不仅集成了如UART等标准通讯模块,此外还集成了LCD驱动器(内建LCD电压稳定电路、电容型偏压电路和电阻型偏压电路),PCA,PWM,10Bit ADC等模块。
为了达到高可靠性和低功耗,SH79F9202内建看门狗定时器,低电压复位功能及系统时钟监控功能。此外SH79F9202还提供了2种低功耗省电模式。
2. 开发环境搭建
- 所需硬件
- SH79F9202U 电路板
- LCD段码液晶屏
- SinoLink Plus 仿真器(见1.文档中的《中颖仿真器使用手册》)
- 所需软件
- Keil C51编译器 C51V961(见2.中颖开发工具包)
- keil插件集成包】Sinowealth_KEIL_Setup_V3.8.13.9.1.7(见2.中颖开发工具包)
- 开发环境安装与配置
- Keil μVision 安装好后要根据需要,代码里面的寄存器初始化和keil里面的代码选项配置保持一致。
- Keil μVision 安装好后要根据需要,代码里面的寄存器初始化和keil里面的代码选项配置保持一致。
3. SH79F9202U单片机资源介绍
基本IO介绍
// Note:配置为输出时,对应的IO上拉需关闭以降低功耗
void IO_Init()
{
P0PCR = 0x00; //端口上拉电阻控制寄存器
P0CR = 0XFF; // 端口控制寄存器 0:输入模式1:输出模式
P0 = 0X00; //端口数据寄存器
P0_4 = 1; // P0.4端口输出高电平
P1PCR = 0X00;
P1CR = 0XFF;
P1 = 0X00;
P2PCR = 0X00;
P2CR = 0XFF;
P2 = 0X00;
P3PCR = 0X00;
P3CR = 0XFE;
P3 = 0X00;
P4PCR = 0X00;
P4CR = 0XFF;
P4 = 0X00;
P5PCR = 0X04; // 开启上拉
P5CR = 0XFB; // P5_2 输入模式
P5 = 0X00; //P5_2 input
}
中断
更加详细的中断介绍还是要仔细看芯片手册,这里为了方便查阅放几张截图。
定时器外设
- 注意:定时器的相关寄存器配置和Keil软件上的程序选项配置保持一致
#include "timer2.h"
// 20ms
void INT_TIMER2(void) interrupt 5
{
static unsigned char counter = 0;
TF2 = 0; // 清除中断标志
++counter;
if(counter >= 25) // 500ms
{
Test_Num++;
// P0_4 = !P0_4; // 翻转输出
counter = 0;
}
LCD_Data_OK(Test_Num);
// LCD显示内容
LCD_Data_Show();
}
void Delays(int del)
{
while(del--);
}
//OSC = 24M
//sysclk OSC / 2 = 12M
void Timer2_Init(void)
{
CLKCON = 0x08; //【打开HFON】内部24MHz RC振荡器
Delays(2000); //至少等待振荡器预热时间
CLKCON |= 0x04; //【选择FS】24M/8M Hz为OSCSCLK
CLKCON |= 0x20; //【分频器CLKS[0:1]】fSYS = fOSCS/2
//12M/12 = 1M = 1us(T=1/f) 定时20ms
P0PCR = 0x00; // 输出模式 关闭上拉电阻
P0CR |= 0X10; // P0_4 GPIO设置为输出模式
T2CON = 0; // 停止定时器2
T2MOD = 0x00; // TCK = 12/12 = 1M 系统时钟的1/12|P1.0时钟输入或IO端口|递增计数
/*如果EXEN2 = 0,定时器2递增到0FFFFH,在溢出后置起TF2位,同时定时器自动将用户软件写好的寄存器RCAP2H和RCAP2L的16位值装入TH2和TL2寄存器。
*/
//初始值 45535 2^16=65536
TH2 = 45535 >> 8; // 取高位(177=1011 0001) = 1011 0001 1101 1111 >> 8
TL2 = 45535 & 0x00FF; // 取低位 (223=1101 1111) = 1011 0001 1101 1111 & 0000 0000 1111 1111
// TH2+TL2 = 1011 0001 1101 1111 =45535
//重载值 45535
RCAP2H = 45535 >> 8;
RCAP2L = 45535 & 0x00FF; //重载值
// 自动重载值=65536-45535=20001
IEN0 |= 0x20; //开中断 定时器2溢出中断
T2CON |= 0x04; //开始定时器2 即寄存器0000 0100
EA = 1; // 开启总中断
// while(1);
}
//IPL1 = 0X01;
//IPH1 = OX01;
4. LCD段码液晶屏参数介绍
- LCD段码液晶屏技术参数
5. 根据LCD液晶屏的真值表写数字译码和驱动程序
根据液晶屏的工作原理和真值表的结构,设计一个数字译码算法,将数字或字符转换为对应的 SEG 和 COM 值。
LCD 真值表
数字译码
基于真值表,编写数字译码表和相应的驱动程序。
源文件 LCD.c
:
#include "LCD.h"
xdata unsigned char LCD_SEG[SEGSIZE] _at_ 0x500; // LCD显示地址 指定位置为0x500
//时间/频率/电压显示 // 0 1 2 3 4 5 6 7 8 9
const unsigned char Disp_Code[]={0xf5,0x05,0xd3,0x97,0x27,0xb6,0xf6,0x15,0xf7,0xb7,// 不带小数点显示
0xfd,0x0d,0xdb,0x9f,0x2f,0xbe,0xfe,0x1d,0xff,0xbf // 带小数点显示
};//显示数字译码
//功率显示 // 0 1 2 3 4 5 6 7 8 9
const unsigned char DispPower_Code[]={0xfd,0x85,0xdb,0x9f,0xa7,0xbe,0xfe,0x95,0xff,0xbf};// 数字译码 "POWER" ":" "."这几个符号常亮
//Note:需在(Keil C51)Option中进行选择LCD为电容型
//为更有效的使用电容型LCD驱动器,用户必须先设置PUMPON,LCDON位之外的所有控制位,然后设置PUMPON位,
//在延迟之后(至少50ms)打开LCD,即LCDON位置1,点亮LCD面板;
void LCD_Init(void)
{
unsigned char i,j;
LCDCON = 0x4F; //0100 1111 LCDON|PUMPON|DUTY(1/4占空比,1/3偏置)|TYPESEL| 1111:VLCD = 1.000VDD 注意:当选择电容型LCD驱动时,此4位无效
LCDCON1 = 0x00; // 电阻型LCD驱动控制器寄存器
DISPCLK0 = 0x00; // LCD驱动时钟分频寄存器 1/4 128K RC 对应的LCD帧频为256/4=64Hz
DISPCLK1 = 0x03; // LCD驱动Pump寄存器 4K
LCDSEL = 0x00; // 闪烁模式控制寄存器 00 无闪烁功能
// 模式选择寄存器
P0SS = 0x83 ; // P0_7/1/0 作为SEG13~15
P1SS = 0xFF ; // P1_7-P1.0作为SEG5~12
P2SS = 0xFF ; // P2_7-P2_4作为COM1~COM4 , P2_3-P2_0 即SEG1~4
P3SS = 0x00 ; // P3_7-P3_0作为I/O,SEG25~32
P4SS = 0x00 ; // P4_6-P4_0作为I/0,SEG18~24
P5SS = 0x0C ; // P5_3/2 作为SEG16/17
for(i=0;i<250;i++) // Delay 50ms 以上再打开LCD 模块
{
for(j=0;j<250;j++);
}
LCDCON |= 0X80; // 打开 LCD 模块
}
void LCD_Show(unsigned char udata)
{
unsigned char i;
for(i=0;i<SEGSIZE;i++)
{
LCD_SEG[i] = udata;
}
}
// 数据处理
void LCD_Data_Show(void){
// 电压 /频率/时间显示
LCD_SEG[0] = Disp_4H; // 千位
LCD_SEG[1] = Disp_4L;
LCD_SEG[2] = Disp_3H; // 百位
LCD_SEG[3] = Disp_3L;
LCD_SEG[4] = Disp_2H; // 十位
LCD_SEG[5] = Disp_2L;
LCD_SEG[6] = Disp_1H; // 个位
LCD_SEG[7] = Disp_1L;
// V H z 时钟符号显示
LCD_SEG[8] = 0x03;
// 功率显示
LCD_SEG[14] = DispPower_3H;
LCD_SEG[13] = DispPower_3L; // 百位
LCD_SEG[12] = DispPower_2H;
LCD_SEG[11] = DispPower_2L; // 十位
LCD_SEG[10] = DispPower_1H;
LCD_SEG[9] = DispPower_1L; // 个位
// 油位显示
LCD_SEG[15] = 0x0f;
LCD_SEG[16] = 0x0f;
//
}
void LCD_Data_OK(unsigned long disp_data){
//
Disp_Data = disp_data;
b4 = Disp_Data /1000%10; // 千位
b3 = Disp_Data /100%10; // 百位
b2 = Disp_Data /10%10; // 十位
b1 = Disp_Data %10; // 个位
// 带小数点显示
// b4 = Disp_Data /1000%10 + 10; // 千位
// b3 = Disp_Data /100%10 + 10; // 百位
// b2 = Disp_Data /10%10 + 10; // 十位
// b1 = Disp_Data %10; // 个位
//------------------------------------------------------------------
Disp_4H = (Disp_Code[b4])>>4;
Disp_4L = (Disp_Code[b4])&0x0F;
Disp_3H = (Disp_Code[b3])>>4;
Disp_3L = (Disp_Code[b3])&0x0F;
Disp_2H = (Disp_Code[b2])>>4;
Disp_2L = (Disp_Code[b2])&0x0F;
Disp_1H = (Disp_Code[b1])>>4; // 个位
Disp_1L = (Disp_Code[b1])&0x0F;
//------------------------------------------------------------------
DispPower_3H = (DispPower_Code[b3])>>4;
DispPower_3L = (DispPower_Code[b3])&0x0F;
DispPower_2H = (DispPower_Code[b2])>>4;
DispPower_2L = (DispPower_Code[b2])&0x0F;
DispPower_1H = (DispPower_Code[b1])>>4;// 个位
DispPower_1L = (DispPower_Code[b1])&0x0F;
if(Disp_Data < 1000) // 当显示数值是"0321"时,不显示前面的"0"
{
Disp_4H = 0;
Disp_4L = 0;
if(Disp_Data < 100)
{
Disp_3H = 0;
Disp_3L = 0;
if(Disp_Data < 10)
{
Disp_2H = 0;
Disp_2L = 0;
}
}
}
}
头文件LCD.h
#ifndef __LCD_H
#define __LCD_H
#include "SH79F9202.h"
#include "cpu.h"
#define SEGSIZE 17
extern xdata unsigned char LCD_SEG[SEGSIZE]; // LCD显示地址 指定位置为0x500
//时间/频率/电压显示
extern const unsigned char Disp_Code[];
//功率显示
extern const unsigned char DispPower_Code[];
static volatile unsigned long Disp_Data=0; // 显示数据
static volatile unsigned char b1,b2,b3,b4; // 存放 个/十/百/千/万位的数值
static volatile unsigned char Disp_1H,Disp_1L,Disp_2H,Disp_2L,Disp_3H,Disp_3L,Disp_4H,Disp_4L; //显示数据预处理值
static volatile unsigned char DispPower_1H,DispPower_1L,DispPower_2H,DispPower_2L,DispPower_3H,DispPower_3L;//显示数据预处理值(功率)
void LCD_Init(void);
void LCD_Show(unsigned char udata);
void LCD_Data_Show(void); // LCD显示内容
void LCD_Data_OK(unsigned long disp_data);
#endif
资料下载
- 参考资料和文献
- 示例代码下载链接
参考资料
- 【CSDN】编程圈子-谢厂节的博客