⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 iopic18.c

📁 单片机c语言程序设计100例--基于PIC+PROTEUS
💻 C
📖 第 1 页 / 共 2 页
字号:
//-----------------------------------------------------------------
// 名称: 输入输出程序
//-----------------------------------------------------------------
#include <pic18.h>
#include <stdlib.h>
#include <stdio.h>
#include "chess.h"
//-----------------------------------------------------------------
// LCD驱动程序
//-----------------------------------------------------------------
void lcd_cls();//清屏
void lcd_blit(WORD addr, const BYTE *img, BYTE num);//棋子绘制函数
void lcd_wrcmd(BYTE cmd);           //写无参命令
void lcd_wrcmd1(BYTE cmd, BYTE arg);//写字节参数命令(或称单参数命令)
void lcd_wrcmd2(BYTE cmd, WORD arg);//写字参数命令  (或称双参数命令)
BYTE scankeypad();                  //键盘扫描子程序
void sleep(INT msecs);              //睡眠子程序
void tone(WORD period, WORD cycles);//声音输出子程序
//LCD命令代码
#define LCD_CURSOR       0x21   //设置光标地址(光标在屏幕上的位置)
#define LCD_OFFSET       0x22   //设置CGRAM偏移地址
#define LCD_ADDRESS      0x24   //设置待显示数据的DDRAM地址
#define LCD_TEXTHOME     0x40   //设置文本区首地址
#define LCD_TEXTAREA     0x41   //设置文本区宽度
#define LCD_GFXHOME      0x42   //设置图形区首地址
#define LCD_GFXAREA      0x43   //设置图形区宽度
#define LCD_ORMODE       0x80   //设置图/文"或"操作模式
#define LCD_XORMODE      0x81   //设置图/文"异或"操作模式
#define LCD_ANDMODE      0x83   //设置图/文"与"操作模式
#define LCD_ATTRMODE     0x84   //设置"属性"模式
#define LCD_DISPLAY      0x90   //设置显示开/关
#define LCD_CLINES       0xA0   //光标线设置(1线,2线,...,8线[块状]光标)
#define LCD_AUTOWRITE    0xB0   //自动写
#define LCD_AUTOREAD     0xB1   //自动读
#define LCD_AUTORESET    0xB2   //自动读/写取消(复位)
#define LCD_WRITEINC     0xC0   //写(地址递增)
#define LCD_READINC      0xC1   //读(地址递增)
#define LCD_WRITEDEC     0xC2   //写(地址递减)
#define LCD_READDEC      0xC3   //读(地址递减)
#define LCD_SCREENPEEK   0xE0   //读屏幕(一字节)
#define LCD_SCREENCOPY   0xE8   //拷贝屏幕(一行)
#define LCD_BITSET       0xF0   //置位操作
//PORTA端口RA0~RA3分别连接WR,RD,CE,C/D
//在PORTA端发送下面字节时分别实现读状态/写命令/写数据/禁止操作
#define LCD_STATUS       0x09   //1001读LCD状态
#define LCD_COMMAND      0x0A   //1100写命令
#define LCD_DATA         0x02   //0010写数据
#define LCD_DONE         0x0F   //1111禁止操作
//国际象棋棋子位图,存放于pieces.c
extern const BYTE LCD_BITMAPS[];
//控制棋子闪烁的时钟嘀嗒数
#define FLASHTICKS 30
//-----------------------------------------------------------------
// 棋盘初始化函数
//-----------------------------------------------------------------
void panel_init()
{
   //初始化I/O端口及其他设置
   PORTA = PORTB = PORTC = PORTD = 0xFF;
   TRISA = TRISC = TRISE = 0;
   RBPU = 0;//PORTB端口内部弱上拉     
   SPEN = 1; CREN = 1;//串口初始化
   SPBRG = 0x19;      //19200 Baud @ 8MHz
   TXSTA = 0xA4;      //CSRC/TXEN (内部时钟,8位模式,异步操作,高速)
   printf("\nProteus VSM Tiny Chess\n");
   //图像屏幕每行32字节(256像素),驻留于地址0
   lcd_wrcmd2(LCD_GFXHOME,  0x0000);    //定义图形区DDRAM首地址
   lcd_wrcmd2(LCD_GFXAREA,  32);        //图形区每行数据占32字节
   lcd_wrcmd2(LCD_TEXTHOME, 0x2000);    //定义文本区DDRAM首地址
   lcd_wrcmd2(LCD_TEXTAREA, 32);        //文本区每行占用32字节(8个字符)
   lcd_wrcmd2(LCD_OFFSET,   0x3000>>11);//定义CGRAM偏移地址(右移11位取得高5位)
   //加载自定义的字符"■"的点阵数据,后面将用每16个这样的字符(4x4=16)构成一个
   //棋盘黑色格子.LCD内置字符编码范围为0x00~0x7F,第1个自定义字符的编码为
   //0x80(10000000),由于CGRAM地址格式为:5+8+3,0x3000>>11已经设置了该地址的
   //高5位为00110,接下来是8位的自定义编码10000000,最后是3位一个字符的8字节点阵
   //数据索引000~111(0~7),故编码为0x80的自定义字符点阵数据在CGRAM中的首地址为:
   //00110-10000000-000,即:0x3400,下面的语句设置了该地址
   lcd_wrcmd2(LCD_ADDRESS,  0x3400);
   //从0x3400地址开始写入8字节全1点阵数据.
   for (INT i = 0; i < 8 ; i++) lcd_wrcmd1(LCD_WRITEINC, 0xFF);  
   lcd_wrcmd(LCD_DISPLAY + 0x0C);  //0C:使能图形与文本显示,禁止光标显示与闪烁
   lcd_wrcmd(LCD_XORMODE);         //图文重叠(混合)时以异或模式显示
}

//-----------------------------------------------------------------
// 清除棋盘:先清屏,然后绘制黑/白交错的棋盘格子
//-----------------------------------------------------------------
void panel_cls()
{ 
   COORD r, c;  lcd_cls();//首先清空屏幕
   //再在256x256的屏幕(32 x 8 = 256)上绘制黑/白交错的棋盘格子
   //每个黑色格子由16个编码为0x80的"■"字符拼凑显示而成
   for (r = 0; r < 32; r += 1)    //全屏可显示32行字符(256/8=32)
   {  //每列也可显示32个字符,通过c+=4使之每显示4个字符后
      //横向跳过4个字符宽度(4个字符宽度 = 棋盘一列的宽度)
      for (c = 0; c < 32; c += 4) 
      {  //下面开始在8行8列(8x8)的棋盘格子上交错显示黑色格子
         //算法说明: 将r/4或c/4可由字符行得到棋盘格行,
         //例如r=0~3为棋盘的第0行(r/4=0),当r=4~7时为棋盘的第1行(r/4=1)
         //棋盘中奇数行偶数列,或偶数行奇数列为黑色格子,故有:
         //if (r / 4 % 2 != c / 4 % 2 )
         //由于/与%运算占用机器时间较多,通过>>或&可消除/与%,因而进一步有:
         if ((r >> 2 & 0x01) != (c >> 2 & 0x01)) //注意加括号提高&的优等级
         //显然,上面的关系式在r与c的二进制位中的第2位不同为0或1为真,
         //由于第2位的掩码为0B00000100 = 0x04,
         //故上面的判断语句还写成下面的语句,通过异或(^)来编写条件式
         //if ((r & 0x04) ^ (c & 0x04))
         {  //设置LCD DDRAM字符显示区起始地址:0x2000 + r * 32 + c
            //(0x2000为字符区的起始地址)
            lcd_wrcmd2(LCD_ADDRESS, 0x2000 + r * 32 + c);
            //在一个黑色棋盘格子内横向显示4个"■"(自定义字符编码为0x80),
            //占据1/4的显示面积,LCD_WRITEINC设置显示地址自动递增
            for (INT i = 0; i < 4; i++) lcd_wrcmd1(LCD_WRITEINC, 0x80);
         }
      } 
   } 
}

//-----------------------------------------------------------------
// 在棋盘的(r,c)位置绘制棋子(棋子p含有棋子名称与颜色信息)
//-----------------------------------------------------------------
void panel_draw(COORD r, COORD c, PIECE p)
{
   const BYTE *sprite;  //指向棋子像素字节的指针sprite
   //矩阵键盘扫描得到的行号是从上到下编号分别为0~7
   //而棋盘格子的行号是由下至上编号分别为0~7,要将键盘扫描得到的
   //行号r转换为棋盘行号r,因而有:
   r = 7 - r;            
   //下面根据p计算出spite,使其指向棋子p的全盘棋子点阵数组中的首地址
   //奇行偶列或偶行奇列为黑色格子,否则为白色格子
   //4位的棋子编码中低3位(X001-X110)标识棋子名称,高位X标识棋子颜色
   //取棋子颜色时&0x08,取棋子名称时&0x07
   BYTE r1 = r & 0x01, c1 = c & 0x01, p1 = p & 0x08;
   //LCD_BITMAPS中的点阵字节为2048,前后各1024字节(1024/32/4=8个棋子图案)
   //前半部分是“白格白子”或“黑格黑子”点阵数据,第0,7号棋子为空
   //点阵中“白格白子”与“黑格黑子”的点阵数据完全相同
   //后半分部是“黑格白子”或“白格黑子”点阵数据(1024/32/4=8个棋子图案)
   //点阵中“黑格白子”或“白格黑子”的点阵数据完全相同   
   if ((r1 != c1 && p1 == WHITE) || (r1 == c1 && p1 == BLACK))
   {  //“黑格白子”或“白格黑子”点阵数据在LCD_BITMAPS的后半部分,首地址为0x0400
      //由于每个棋子点阵横向占4字节,故有*4,更详细说明参考LCD_BITMAPS的取模说明.
      sprite = LCD_BITMAPS + (p & 0x07) * 4 + 0x0400; 
   }
   else
   {  //“白格白子”或“黑格黑子”点阵首地址在LCD_BITMAPS前半部分
      sprite = LCD_BITMAPS + (p & 0x07) * 4;  
   }
   panel_blit(r, c, sprite);  //调用底层函数绘制棋子像素位
}
 
//-----------------------------------------------------------------
// 将指定棋子图像绘制在棋盘上的底层函数
//-----------------------------------------------------------------
void panel_blit(COORD r, COORD c, const BYTE *sprite)
{ 
   //棋子点阵数组LCD_BITMAPS中每个棋子图像像素为:32x32,占32x32/8=128字节,
   //每行8个棋子,故有一整行棋子点阵总字节数为:128x8=1024字节
   //棋子点阵为32x32,即每个棋子图像32行,每行32个像素点占32/8=4字节
   //下面首先根据棋盘(r,c)得到棋子点阵首地址:
   WORD addr = r * 1024 + c * 4;
   //从指定地址开始绘制一个棋子的32行像素
   for (INT i = 0; i < 32; i++)
   {   //每循环一次绘制棋子32行像素中的一行(4字节,32个像素)
       lcd_blit(addr, sprite, 4); 
       //然后将显示DDRAM地址及取像素地址分别递增32字节
       //跳到下一行的DDRAM地址和下一行的取点阵地址
       addr += 32;  sprite += 32;
   }         
 }

//-----------------------------------------------------------------
// 选取待移动的棋子,如果点击的待移入位置是新位置则返回真
//-----------------------------------------------------------------
BOOL panel_getmove(BYTE from[], BYTE to[])
{  BYTE key, fdelay, fstate = 0; PIECE p;   
   //在用户首次移动后设置随机种子
   if (movecount == 1) srand (TMR1);
   //扫描键盘(即8x8的触模屏区)得到键值key
   key = scankeypad();
   //返回有效编码范围为0~63,无键按下时返回FALSE
   if (key == 0xFF) return FALSE;
   //根据键值计算得出待移动棋子的位置(行/列)
   from[0] = key / 8; from[1] = key % 8; 
   //根据待移动棋子当前位置得到棋子p("7-"的原因见上一函数说明)
   p = board[7 - from[0]][from[1]];
   //如果棋子为空(点击是无棋子的格子)或棋子为黑子(因为已方执白)
   //则直接返回FALSE,否则继续下一步处理
   if (p == EMPTY || p & BLACK) return FALSE;  
   fdelay = 0;
   //如果选子按键未释放
   while (scankeypad() != 0xFF)  
   {   //所选中的棋子在原位通过反复反相,形成闪烁显示
       if (fdelay-- == 0)
       { panel_invert(from, fstate ^= 1);  fdelay = FLASHTICKS;
       }
   }
   //如果选子按键已经释放
   while ((key = scankeypad()) == 0xFF)  
   {   //所选中的棋子仍在原位通过反复反相,继续闪烁显示
       if (fdelay-- == 0)
       { panel_invert(from, fstate ^= 1);  fdelay = FLASHTICKS;
       }
   }
   //闪烁停止,如果fstate为1时则表示棋子是在奇数次反相后停止的,
   //此时棋子已不同于原样了,故要再执行一次反相,还原出其原始特征
   if (fstate) panel_invert(from, 0);
   //根据键值得到目标位置的键盘行/列号
   to[0] = key / 8;  to[1] = key % 8; 
   //将起始键盘行号转换为起始格子行号,将目标键盘行号转换为目标格子行号
   from[0] = 7 - from[0]; to[0] = 7 - to[0];
   //等待按键释放
   while (scankeypad() != 0xFF);
   //当待移入的是新位置时返回真
   return from[0] != to[0] || from[1] != to[1];
}

//-----------------------------------------------------------------

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -