本文为大大维原创,最早于博客园发表,转载请注明出处!!!
一、实验目的和要求
掌握点阵式液晶显示屏的原理和控制方法,掌握点阵字符的显示方法。
掌握模拟/数字(A/D)转换方式,
进一步掌握使用C51语言编写程序的方法,使用C51语言编写实现重量测量的功能。
二、实验设备
单片机测控实验系统
重量测量实验板/砝码
Keil开发环境
STC-ISP程序下载工具
三、实验内容
编写C51程序,使用重量测量实验板测量标准砝码的重量,将结果(以克计)显示到液晶屏上。误差可允许的范围之间。
四、实验步骤
1. 阅读实验原理,掌握YM12864C的控制方式,编写出基本的输出命令和数据的子程序;
2. 掌握点阵字模的构成方式。使用字模软件PCtoLCD2002,设定正确的输出模式,生成点阵数据;
3. 使用C51语言编写重量测量程序;
4. 调零,满量程校准;
5. 将编译后的程序下载到51单片机;
6. 在托盘中放上相应重量的法码,使显示值为正确重量。
五、实验原理
1. 参考下文,学习点阵式液晶显示屏的控制方法。
2. 在液晶显示中,自定义图形和文字的字模对应的字节表需要使用专门的字模软件来生成。可以使用PCtoLCD2002字模软件提取。
3. 字符点阵等数据,需要定义在code数据段中,具体原理参见示例程序设计部分。
4. 向LCM输出一个命令或数据时,应当在选通信号为高时准备好数据,然后延迟若干指令周期,再将选通信号置为低。
5. 与A/D转换相关的寄存器
ADC_POWER:ADC电源控制位,0关1开。
SPEED1,SPEED0:模数转换器速度控制位,控制A/D转换所需时间。
ADC_FLAG:模数转换结束标志位,AD转换完后,ADC_FLAG=1,一定要软件清0。
ADC_START:模数转换器(ADC)转换启动控制位,1开始转换,转换结束后为0。
CHS2/CHS1/CHS0:模拟输入通道选择,选择使用P1.0~P1.7作为A/D输入。
ADC_RES、ADC_RESL: A/D转换结果寄存器,是特殊功能寄存器,用于保存A/D转换结果。
IE:中断允许寄存器(可位寻址)
EA:CPU的中断开放标志,EA=1,CPU开放中断,EA=0,CPU屏蔽所有中断申请。
EADC:A/D转换中断允许位。1允许0禁止。
IPH:中断优先级控制寄存器高(不可位寻址)。
IP:中断优先级控制寄存器低(可位寻址)。
A/D转换器的具体使用方法参见STC单片机指南的第九章。
6. 重量传感器采用压敏电阻。利用压敏电阻采集应变,产生变化的阻值。利用放大电路将其转化为电压值,通过数模转换将电压值转化成CPU处理的数字信号。传感器根据编制的程序将数字信号转换为砝码重量显示输出。
7.本实验示意电路原理图:
六、实验代码
1 #include<reg52.h> 2 #include<intrins.h> // 声明了void _nop_(void) 3 #define LcdData P2 //LCD数据口(8线接法) 4 typedef unsigned int uint; 5 typedef unsigned char uchar; 6 7 8 //*************AD端口定义及相关函数声明************** 9 sfr PLASF = 0X9D; //P1口模拟功能控制寄存器P1ASF 10 sfr ADC_CONTR = 0XBC; //ADC控制寄存器 11 sfr ADC_RES = 0XBD; //A/D转换结果寄存器高 12 sfr ADC_RESL = 0XBE; //A/D转换结果寄存器低 13 sfr AUXR1 = 0XA2; 14 sfr IPH = 0XB7; //中断优先级控制寄存器高 15 bit EADC = 0XA8^5; //A/D转换中断允许位 16 #define ADC_POWER 0X80 //ADC电源控制位 17 #define ADC_FLAG 0X10 //模数转换器(ADC)转换启动控制位 18 #define ADC_START 0X08 19 #define ADC_SPEEDLL 0X00 20 #define ADC_SPEEDL 0X20 21 #define ADC_SPEEDH 0X40 22 #define ADC_SPEEDHH 0X60 23 24 //AD函数声明 25 uint ADC_GET(uchar n); //获得ADCData 26 void DelayMs(uint n); //软件延迟n毫秒 27 void InitAD(uchar n); //AD初始化 28 void DelayUs(uint cnt); //延时cnt us(NOP) 29 30 //*********LCD端口定义及相关函数声明*************** 31 sbit RST = P1^5; 32 sbit CS1 = P1^7; //左半屏选择 33 sbit CS2 = P1^6; //右半屏选择 34 sbit E = P3^3; //使能 35 sbit RW = P3^4 ; //读/写选择器引脚(R/W) 36 sbit RS = P3^5; //数据/命令选择器引脚(R/S) 37 sbit BUSY = P2^7; //BUSY位 38 uchar mypage=2; //汉字显示页设置 39 //C51并不支持位数组 40 //sbit DB[] = {P2^0, P2^1, P2^2, P2^3, P2^4, P2^5, P2^6, P2^7}; 41 42 //LCD函数声明 43 void LcdCommandWrite(uchar value); //写指令代码函数 44 void LcdDataWrite(uchar value); //写显示数据函数 45 void LcdCommandRead(void); //读状态字函数 46 //void LcdDataRead(void); //读显示数据函数 47 //C51中没有布尔类型 48 void IsBusy(void); //读状态字时判断BUSY是否为1 49 void ShowOn(void); //显示开 50 void ShowOff(void); //显示关 51 void SetRow(uchar row); //设置显示行 52 void SetCol(uchar col); //设置显示列 53 void SetPage(uchar page); //设置显示页 54 void ScreenChoose(uint screen); //屏幕显示选择 55 void Clear(uint screen); //清选定的屏 56 void DisplayWord(uint screen,uchar page,uchar col,uchar *word); //显示汉字,按页显示 57 void DisplayChar(uint screen,uchar row,uchar col); //显示字符,按行显示 58 void Display(uint weight); //重量显示 59 void Delay(uint cnt); //延迟函数 60 void DisplayLine(void); //显示一条直线 61 void InitLCD(void); //LCD初始化 62 63 64 //*********LCD字码*************** 65 uchar code zhong[]={0x10,0x10,0x14,0xD4,0x54,0x54,0x54,0xFC,0x52,0x52,0x52,0xD3,0x12,0x10,0x10,0x00,0x40,0x40,0x50,0x57,0x55,0x55,0x55,0x7F,0x55,0x55,0x55,0x57,0x50,0x40,0x40,0x00}; 66 uchar code liang[]={0x20,0x20,0x20,0xBE,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xBE,0x20,0x20,0x20,0x00,0x00,0x80,0x80,0xAF,0xAA,0xAA,0xAA,0xFF,0xAA,0xAA,0xAA,0xAF,0x80,0x80,0x00,0x00}; 67 uchar code wei[]={0x00,0x20,0x22,0x2C,0x20,0x20,0xE0,0x3F,0x20,0x20,0x20,0x20,0xE0,0x00,0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x06,0x01,0x00,0x01,0x46,0x80,0x40,0x3F,0x00,0x00,0x00}; 68 uchar code maohao[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 69 uchar code ling[]={0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 70 uchar code yi[]={0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 71 uchar code er[]={0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 72 uchar code san[]={0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 73 uchar code si[]={0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 74 uchar code wu[]={0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 75 uchar code liu[]={0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 76 uchar code qi[]={0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 77 uchar code ba[]={0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 78 uchar code jiu[]={0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 79 uchar code ke[]={0x04,0x04,0xE4,0x24,0x24,0x24,0x24,0x3F,0x24,0x24,0x24,0x24,0xE4,0x04,0x04,0x00,0x80,0x80,0x43,0x22,0x12,0x0E,0x02,0x02,0x02,0x7E,0x82,0x82,0x83,0x80,0xE0,0x00}; 80 81 82 //*********main函数*************** 83 void main() 84 { 85 InitAD(0); 86 InitLCD(); 87 DisplayLine(); 88 while(1) 89 { 90 uint ad = 0; 91 ad = ADC_GET(0); 92 Display(ad); 93 } 94 } 95 96 //*********AD函数定义*************** 97 void InitAD(uchar n) 98 { 99 100 PLASF = 0xff; 101 ADC_RES = 0; 102 ADC_CONTR = ADC_POWER | ADC_SPEEDLL; 103 DelayMs(2); 104 105 106 n &= 0x07; 107 AUXR1 |= 0x04; 108 PLASF = 1<<n; 109 110 PLASF = 0X01; 111 ADC_CONTR = 0X10; 112 EA = 1; 113 ADC_RES = 0X00; 114 ADC_RESL = 0X00; 115 EADC = 1; //AD中断开 116 117 } 118 uint ADC_GET(uchar n) 119 { 120 121 uint ADCData; 122 n &= 0x07; 123 ADC_RES = 0; 124 ADC_RESL = 0; 125 ADC_CONTR = 0; //ADC_CONTR置0 126 ADC_CONTR |= (ADC_POWER|ADC_SPEEDLL|n|ADC_START); 127 DelayUs(6); //延迟6个nop,书上4个nop,不够 128 while(!((ADC_CONTR & ADC_FLAG) == 0x10)) 129 ADCData = (ADC_RES&0x03)*256 + ADC_RESL; //取十位结果 130 return ADCData-(636+99); //返回ADCData 131 } 132 133 134 void DelayMs(uint n) 135 { 136 uint x; 137 while(n--) 138 { 139 x = 5000; 140 while(x--); 141 } 142 } 143 144 //*********LCD函数定义*************** 145 void InitLCD(void) 146 { 147 IsBusy(); 148 RST=1; 149 RST=0; //复位 150 Delay(100); 151 RST=1; 152 ShowOff(); 153 Delay(100); 154 ShowOn(); 155 Clear(0); 156 //设置起始位置(0,0) 157 SetPage(0); 158 SetRow(0); 159 SetCol(0); 160 } 161 void ScreenChoose(uint screen){ 162 switch(screen) 163 { 164 case 0: CS1=1;CS2=1;break; //全屏 165 case 1: CS1=1;CS2=0;break; //左屏 166 case 2: CS1=0;CS2=1;break; //右屏 167 default: CS1=1;CS2=1;break; //全屏 168 } 169 } 170 171 void LcdCommandWrite(uchar value) //写指令代码函数 172 { 173 IsBusy(); 174 // 定义相关引脚 175 //CS1=1; 176 //CS2=1; 177 RS=0; 178 RW=0; 179 LcdData=value; 180 //一个下跳沿 181 E=1; 182 Delay(200); 183 E=0; 184 } 185 void LcdDataWrite(uchar value) //写显示数据函数 186 { 187 IsBusy(); 188 //CS1=1; 189 //CS2=1; 190 RS=1; 191 RW=0; 192 LcdData=value; 193 E=1; 194 Delay(200); 195 E=0; 196 } 197 void LcdCommandRead(void) //读状态字函数 198 { 199 RS=0; 200 RW=1; 201 E=1; 202 } 203 /* 204 void LcdDataRead(void) //读显示数据函数 205 { 206 RS=1; 207 RW=1; 208 E=1; 209 } 210 */ 211 void IsBusy(void) //判断BUSY是否为1 212 { 213 LcdCommandRead(); 214 while(BUSY); 215 E=0; //控制LCM读取结束 216 } 217 void DelayUs(uint cnt) 218 { 219 uint temp=0; 220 for(temp=0;temp<cnt;temp++) 221 /* 222 *对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数相当汇编NOP指令,延时几微秒。 223 *NOP指令为单周期指令,可由晶振频率算出延时时间,对于12M晶振,延时1uS。 224 */ 225 _nop_(); 226 } 227 void Delay(uint cnt) 228 { 229 while(--cnt); 230 } 231 void ShowOn(void) //显示开 232 { 233 LcdCommandWrite(0x3f); 234 } 235 void ShowOff(void) //显示关 236 { 237 LcdCommandWrite(0x3e); 238 } 239 void SetRow(uchar row) //设置显示行 240 { 241 row=row&0x3f; //Row范围0到63,高两位清零 242 LcdCommandWrite(0xc0+row); 243 } 244 void SetCol(uchar col) //设置显示列 245 { 246 col=col&0x3f; //Col范围0到63,高两位清零 247 LcdCommandWrite(0x40+col); 248 } 249 void SetPage(uchar page) //设置显示页 250 { 251 page=page&0x07; //Page范围0到7,取低三位 252 LcdCommandWrite(0xb8+page); 253 } 254 void Clear(uint screen){ 255 uint i,j; 256 ScreenChoose(screen); 257 for(i=0;i<8;i++){ 258 SetPage(i); 259 SetCol(0x00); 260 for(j=0;j<64;j++){ 261 LcdDataWrite(0x00); 262 } 263 } 264 } 265 void DisplayWord(uint screen,uchar page,uchar col,uchar *word) //显示汉字,按页显示 266 { 267 uint i=0; 268 //显示汉字,16*16,需要两页 269 ScreenChoose(screen); 270 SetPage(page); //选上半字 271 SetCol(col); //选定列数 272 for(i=0;i<16;i++){ //上半个字s 273 LcdDataWrite(word[i]); 274 } 275 SetPage(page+1); //选下半字 276 SetCol(col); //选定列数 277 for(i=0;i<16;i++){ //下半个字 278 LcdDataWrite(word[i+16]); 279 } 280 } 281 282 void DisplayChar(uint screen,uchar row,uchar col) //显示字符,按行显示 283 { 284 ScreenChoose(screen); 285 SetRow(row); //选定行数 286 SetCol(col); //选定列数 287 LcdDataWrite(0x01); 288 } 289 290 291 void Display(uint weight){ 292 293 uchar a,b,c; 294 DisplayWord(1,mypage,0*16,zhong); //重 295 DisplayWord(1,mypage,1*16,liang); //量 296 DisplayWord(1,mypage,2*16,wei); //为 297 DisplayWord(1,mypage,3*16,maohao); //: 298 a = weight/100; 299 b = weight%100/10; 300 c = weight%10; 301 switch(a){ 302 case 0: DisplayWord(2,mypage,0*16,ling);break; 303 case 1: DisplayWord(2,mypage,0*16,yi);break; 304 case 2: DisplayWord(2,mypage,0*16,er);break; 305 case 3: DisplayWord(2,mypage,0*16,san);break; 306 case 4: DisplayWord(2,mypage,0*16,si);break; 307 case 5: DisplayWord(2,mypage,0*16,wu);break; 308 case 6: DisplayWord(2,mypage,0*16,liu);break; 309 case 7: DisplayWord(2,mypage,0*16,qi);break; 310 case 8: DisplayWord(2,mypage,0*16,ba);break; 311 case 9: DisplayWord(2,mypage,0*16,jiu);break; 312 } 313 switch(b){ 314 case 0: DisplayWord(2,mypage,1*16,ling);break; 315 case 1: DisplayWord(2,mypage,1*16,yi);break; 316 case 2: DisplayWord(2,mypage,1*16,er);break; 317 case 3: DisplayWord(2,mypage,1*16,san);break; 318 case 4: DisplayWord(2,mypage,1*16,si);break; 319 case 5: DisplayWord(2,mypage,1*16,wu);break; 320 case 6: DisplayWord(2,mypage,1*16,liu);break; 321 case 7: DisplayWord(2,mypage,1*16,qi);break; 322 case 8: DisplayWord(2,mypage,1*16,ba);break; 323 case 9: DisplayWord(2,mypage,1*16,jiu);break; 324 } 325 switch(c){ 326 case 0: DisplayWord(2,mypage,2*16,ling);break; 327 case 1: DisplayWord(2,mypage,2*16,yi);break; 328 case 2: DisplayWord(2,mypage,2*16,er);break; 329 case 3: DisplayWord(2,mypage,2*16,san);break; 330 case 4: DisplayWord(2,mypage,2*16,si);break; 331 case 5: DisplayWord(2,mypage,2*16,wu);break; 332 case 6: DisplayWord(2,mypage,2*16,liu);break; 333 case 7: DisplayWord(2,mypage,2*16,qi);break; 334 case 8: DisplayWord(2,mypage,2*16,ba);break; 335 case 9: DisplayWord(2,mypage,2*16,jiu);break; 336 } 337 DisplayWord(2,mypage,3*16,ke); 338 Delay(5000); 339 } 340 void DisplayLine(void) //显示一条直线 341 { 342 DisplayChar(1,50,0); 343 DisplayChar(1,50,1); 344 DisplayChar(1,50,2); 345 DisplayChar(1,50,3); 346 347 DisplayChar(1,50,8); 348 DisplayChar(1,50,9); 349 DisplayChar(1,50,10); 350 DisplayChar(1,50,11); 351 352 DisplayChar(1,50,16); 353 DisplayChar(1,50,17); 354 DisplayChar(1,50,18); 355 DisplayChar(1,50,19); 356 357 DisplayChar(1,50,24); 358 DisplayChar(1,50,25); 359 DisplayChar(1,50,26); 360 DisplayChar(1,50,27); 361 362 DisplayChar(1,50,32); 363 DisplayChar(1,50,33); 364 DisplayChar(1,50,34); 365 DisplayChar(1,50,35); 366 367 DisplayChar(1,50,40); 368 DisplayChar(1,50,41); 369 DisplayChar(1,50,42); 370 DisplayChar(1,50,43); 371 372 DisplayChar(1,50,48); 373 DisplayChar(1,50,49); 374 DisplayChar(1,50,50); 375 DisplayChar(1,50,51); 376 377 DisplayChar(1,50,56); 378 DisplayChar(1,50,57); 379 DisplayChar(1,50,58); 380 DisplayChar(1,50,59); 381 382 DisplayChar(2,50,0); 383 DisplayChar(2,50,1); 384 DisplayChar(2,50,2); 385 DisplayChar(2,50,3); 386 387 DisplayChar(2,50,8); 388 DisplayChar(2,50,9); 389 DisplayChar(2,50,10); 390 DisplayChar(2,50,11); 391 392 DisplayChar(2,50,16); 393 DisplayChar(2,50,17); 394 DisplayChar(2,50,18); 395 DisplayChar(2,50,19); 396 397 DisplayChar(2,50,24); 398 DisplayChar(2,50,25); 399 DisplayChar(2,50,26); 400 DisplayChar(2,50,27); 401 402 DisplayChar(2,50,32); 403 DisplayChar(2,50,33); 404 DisplayChar(2,50,34); 405 DisplayChar(2,50,35); 406 407 DisplayChar(2,50,40); 408 DisplayChar(2,50,41); 409 DisplayChar(2,50,42); 410 DisplayChar(2,50,43); 411 412 DisplayChar(2,50,48); 413 DisplayChar(2,50,49); 414 DisplayChar(2,50,50); 415 DisplayChar(2,50,51); 416 417 DisplayChar(2,50,56); 418 DisplayChar(2,50,57); 419 DisplayChar(2,50,58); 420 DisplayChar(2,50,59); 421 }
七、一点想法
LCD12864控制输出时,注意行、列、页之间的 关系。
使用_nop_()函数可以精确控制延迟时间。
八、附录
STC单片机指南:点此查看
实验电路原理图:点击查看