/*分享:HT46R94 直接驱动 1/2 BIAS LCD 显示屏的完整C语言源代码
张贴于: 23 Apr 2009 10:36 PM
====调试了三天, 终于给我调试出来了,呵呵,谢谢HT的宋工====
实验: HT46R94 直接驱动 1/2 BIAS, 4COM, 12SEG LCD 显示屏
MCU: Holtek HT46R94 仿真器+接口卡
IDE: HT-IDE3000 V7.0 Beta build 1105
编译器: 选择 Enhanced Holtek C Compiler/Assembler
@author: JimmyChen, jimweaver@126.com
@modified: 2009-04-22 15:22
*/
/*
根据 Holtek 网站 App Notes "ha0168s" 中的 Format 2 汇编代码改编
Configuration Options: 时基中断频率=fs/2^5, 其余采用默认设置
实测结果: LCD 显示正常, 十进制计数器
*/
#include "HT46R94.h"
//LCD引脚定义
//SEG引脚
#define LCD_SEG0 _pc0
#define LCD_SEG1 _pc1
#define LCD_SEG2 _pc2
#define LCD_SEG3 _pc3
#define LCD_SEG4 _pe0
#define LCD_SEG5 _pe1
#define LCD_SEG6 _pe2
#define LCD_SEG7 _pe3
#define LCD_SEG8 _pe4
#define LCD_SEG9 _pe5
#define LCD_SEG10 _pe6
#define LCD_SEG11 _pd0
//COM引脚
#define LCD_COM0 _pc4
#define LCD_COM1 _pc5
#define LCD_COM2 _pc6
#define LCD_COM3 _pc7
#pragma vector isr_timebase @ 0x10 //时基中断向量, 0x0c if HT46R92
//
bit display_flag; //是否刷新显示的标志
unsigned char com_pulse; //COM扫描周期循环计数器,一个扫描周期=8次中断
unsigned char count[6]; //待显示的数值
unsigned char display_codes[6]; //显示缓冲区,存放待显示的数值的段位码
unsigned char time_count; //时基中断计数器
//LCD显示屏对应的段位码表
const unsigned char lcd_table[] = {
0x5f, //0, LCD段位码格式为: D7~D0=(P,E,G,F,D,C,B,A)
0x06, //1
0x6b, //2
0x2f, //3
0x36, //4
0x3d, //5
0x7d, //6
0x07, //7
0x7f, //8
0x3f, //9
0x77, //A
0x7c, //b
0x59, //C
0x6e, //d
0x79, //E
0x71, //F
0x58, //L, index:0x10
0x76, //H, index:0x11
0xff, //全亮, index:0x12
0x00 //空白, index:0x13
};
const unsigned char LCDC_MASKS[] =
{
//LCDC寄存器设置掩码
0x3e, //11111110, rsel=1, lcden=1, com0en=0
0x3d, //11111101, rsel=1, lcden=1, com1en=0
0x3b, //11111011, rsel=1, lcden=1, com2en=0
0x37 //11110111, rsel=1, lcden=1, com3en=0
};
void isr_timebase(void)
{
//时基中断
display_flag = 1;
com_pulse++;
time_count++;
}
void get_display_number();
void init()
{
//复位初始值: PA=0xff,PAC=0xff,PB=0xff,PBC=0xff
//PC=0x00,PD=0xff,PE=0xff,LCDC=0x00
_pd0 = 0;//LCD_SEG11
_pe = 0;//LCD_SEG10 ~ LCD_SEG4
display_flag = 0;
com_pulse = 0;
time_count = 0;
//初始化计数器各位
count[0] = 0;
count[1] = 0;
count[2] = 0;
count[3] = 0;
count[4] = 0;
count[5] = 0;
//初始化 Timebase
//
_etbi = 1;//时基中断
_emi = 1;//开中断
//_lcdc = 0b00011111;//b5=rsel:0,选择2x100k偏压电阻;b4=lcden:1,使能LCD;b3~b0:强制COM输出VDD/2
/*
rsel: Select resistor for R type LCD bias current:
1: 2x50k(1/2bias),Ibias=50uA at Vdd=5V
0: 2x100k(1/2bias),Ibias=25uA at Vdd=5V
*/
_rsel = 0; //选择2x100k偏压电阻
_lcden = 1; //使能LCD驱动
}
//set com0~com3 && segments output
void lcd_display()
{
unsigned char mask, i;
display_flag = 0;
//分 8 拍依次设置COM0~3的输出,每个SEG引脚输出VDD还是输出VSS需要根据要显示的数字判断
//先产生COMs,再产生SEGs
if(com_pulse < 5)
{
//扫描周期之1~4拍
//前4拍: COM0=1 -> COM1=1 -> COM2=1 -> COM3=1, 其余COM?=VDD/2, 若要亮某点: SEG?=0
mask = LCDC_MASKS[com_pulse - 1];
_lcdc = mask;//设置COM引脚状态
LCD_COM0 = 1;
LCD_COM1 = 1;
LCD_COM2 = 1;
LCD_COM3 = 1;
//产生SEGs, 若要亮某点: SEG?=0
//if display_codes[0].0 == 1 then seg0 = 0 else seg0 = 1
//if((display_codes[0] && 0x01)) LCD_SEG0 = 0; else LCD_SEG0 = 1;
LCD_SEG0 = !(display_codes[0] & 0x10);//D4
LCD_SEG1 = !(display_codes[0] & 0x01);//D0
LCD_SEG2 = !(display_codes[1] & 0x10);//D4
LCD_SEG3 = !(display_codes[1] & 0x01);//D0
LCD_SEG4 = !(display_codes[2] & 0x10);//D4
LCD_SEG5 = !(display_codes[2] & 0x01);//D0
LCD_SEG6 = !(display_codes[3] & 0x10);//D4
LCD_SEG7 = !(display_codes[3] & 0x01);//D0
LCD_SEG8 = !(display_codes[4] & 0x10);//D4
LCD_SEG9 = !(display_codes[4] & 0x01);//D0
LCD_SEG10 = !(display_codes[5] & 0x10);//D4
LCD_SEG11 = !(display_codes[5] & 0x01);//D0
}
else
{
//扫描周期之5~8拍
//后4拍: COM0=0 -> COM1=0 -> COM2=0 -> COM3=0, 其余COM?=VDD/2, 若要亮某点: SEG?=1
if (com_pulse == 5)
{
//第5拍时,要把前面4拍4次循环右移后的值,通过调换高低4位来恢复原状
_swap(&display_codes[0]);
_swap(&display_codes[1]);
_swap(&display_codes[2]);
_swap(&display_codes[3]);
_swap(&display_codes[4]);
_swap(&display_codes[5]);
}
mask = LCDC_MASKS[com_pulse - 5];
_lcdc = mask;//设置COM引脚状态
LCD_COM0 = 0;
LCD_COM1 = 0;
LCD_COM2 = 0;
LCD_COM3 = 0;
//产生SEGs, 若要亮某点: SEG?=1
//if display_codes[0].0 == 1 then seg0 = 1 else seg0 = 0
LCD_SEG0 = (display_codes[0] & 0x10);//D4
LCD_SEG1 = (display_codes[0] & 0x01);//D0
LCD_SEG2 = (display_codes[1] & 0x10);//D4
LCD_SEG3 = (display_codes[1] & 0x01);//D0
LCD_SEG4 = (display_codes[2] & 0x10);//D4
LCD_SEG5 = (display_codes[2] & 0x01);//D0
LCD_SEG6 = (display_codes[3] & 0x10);//D4
LCD_SEG7 = (display_codes[3] & 0x01);//D0
LCD_SEG8 = (display_codes[4] & 0x10);//D4
LCD_SEG9 = (display_codes[4] & 0x01);//D0
LCD_SEG10 = (display_codes[5] & 0x10);//D4
LCD_SEG11 = (display_codes[5] & 0x01);//D0
}
//将各个段位码循环右移一位,为下一次扫描准备
_rr(&display_codes[0]);
_rr(&display_codes[1]);
_rr(&display_codes[2]);
_rr(&display_codes[3]);
_rr(&display_codes[4]);
_rr(&display_codes[5]);
if(com_pulse == 8)
{
//扫描次数==8, 一个扫描周期结束
com_pulse = 0;//从0开始计数
}
}
//查得段位码, 原汇编中子程序名为 display_number
void get_seg_codes()
{
unsigned char i, code, size;
size = sizeof(count);
for(i = 0; i < size; i++)
{
code = lcd_table[count[i]];//根据待显示的数值,查段码表,得到段位码,
display_codes[i] = code; //存入段位码显示缓冲区
}
}
//十进制计数器,得到变化的样例数值
void get_display_number()
{
if(time_count == 255)
{
//255个timebase 计数,不到0.5秒
time_count = 0;
count[5]++;//十进制计数,由个位数加起
if(count[5] == 10)
{
//个位满十,向十位进1
count[5] = 0;
count[4]++;//进位
if(count[4] == 10)
{
//十位满十,向百位进1
count[4] = 0;
count[3]++;
if(count[3] == 10)
{
//百位满十,向千位进1
count[3] = 0;
count[2]++;
if(count[2] == 10)
{
//千位满十,向万位进1
count[2] = 0;
count[1]++;
if(count[1] == 10)
{
//万位满十,向十万位进1
count[1] = 0;
count[0]++;
}
}
}
}
}
}
}
//主程序
void main()
{
init();//初始化
while(1)
{
//无穷循环
_clrwdt();
get_display_number();//做十进制计数
if(com_pulse == 0) //每个扫描周期开始前,先查得待显示数据的段位码
get_seg_codes();//查得段位码
if(display_flag) //若需要刷新显示
lcd_display(); //显示到LCD
}
}