12864c5.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. //12864串行时序的实现
  2. ///**************************************/
  3. //#include<reg51.h>
  4. //#include<intrins.h>
  5. #include<math.h>
  6. #include "sys.h"
  7. /**************************************/
  8. #define uchar unsigned char
  9. #define uint unsigned int
  10. #define clear 0x01 //清屏
  11. #define reset_DDRAM 0x02 //DDRAM地址归位
  12. #define left_move 0x04 //游标左移
  13. #define right_move 0x06 //游标右移
  14. #define all_left_move 0x05 //画面整体左移
  15. #define all_right_move 0x07 //画面整体右移
  16. #define display_left_move 0x10 //显示游标左移
  17. #define display_right_move 0x14 //显示游标右移
  18. #define set_function1 0x30 //基本指令集动作
  19. #define set_CGRAM 0x40 //设定CGRAM地址
  20. #define set_DDRAM 0x80 //设定DDRAM地址
  21. #define set_function2 0x34 //扩充指令集动作
  22. #define fanbai 0x04 //反白第一行(扩充指令集)
  23. #define set_GDRAM 0x80 //设定GDRAM地址(扩充指令集)
  24. #define ON_G 0x36 //开绘图显示(扩充指令集)
  25. #define set_function2 0x34 //关绘图显示(扩充指令集)
  26. //端口定义
  27. sbit LCD_CS = P1 ^ 3;
  28. sbit LCD_SID = P1 ^ 1; //串行数据线
  29. sbit LCD_SCLK = P1 ^ 2; //串上时钟输入
  30. uchar const a[] = {"QC12864"}; //定义要显示的字符串
  31. /**************************************/
  32. //延时函数
  33. /**************************************/
  34. void Delay_nms ( uchar n )
  35. {
  36. uchar i;
  37. uchar j;
  38. for ( i = 0; i < n; i++ )
  39. for ( j = 0; j < 125; j++ ) //大概1ms
  40. _nop_();
  41. }
  42. /**************************************/
  43. //串行发送一个字节
  44. /**************************************/
  45. void LCD_sendbyte ( uchar byte )
  46. {
  47. uchar i;
  48. for ( i = 0; i < 8; i++ )
  49. {
  50. LCD_SCLK = 0; //拉低时钟线
  51. _nop_();
  52. LCD_SID = ( bit ) ( byte & 0x80 ); //发送最高位数据
  53. LCD_SCLK = 1; //上升沿发送数据
  54. byte = byte << 1; //左移一位
  55. }
  56. }
  57. /****************************************/
  58. //写指令
  59. /****************************************/
  60. void LCD_write_com ( uchar com )
  61. {
  62. LCD_CS = 1;
  63. LCD_sendbyte ( 0xf8 ); //送入5个连续的“1“,启动一个周期,11111,RW(0),RS(0),0
  64. LCD_sendbyte ( 0xf0 & com ); //取高四位,数据分两次传送,
  65. //每个字节的内容被送入两个字节
  66. //高四位放在第一个字节的高四位
  67. LCD_sendbyte ( 0xf0 & ( com << 4 ) ); //低四位放在第二个字节的高四位
  68. LCD_CS = 0;
  69. Delay_nms ( 10 ); //串行不支持读操作,不可检测忙操作,这里用延时替代
  70. }
  71. /******************************************/
  72. //写数据
  73. /******************************************/
  74. void LCD_write_dat ( uchar dat )
  75. {
  76. LCD_CS = 1;
  77. LCD_sendbyte ( 0xfa ); //送入5个连续的“1“,启动一个周期,11111,RW(0),RS(1),0
  78. LCD_sendbyte ( 0xf0 & dat ); //取高四位,数据分两次传送,
  79. //每个字节的内容被送入两个字节
  80. //高四位放在第一个字节的高四位
  81. LCD_sendbyte ( 0xf0 & ( dat << 4 ) ); //低四位放在第二个字节
  82. LCD_CS = 0;
  83. Delay_nms ( 10 );
  84. }
  85. /********************************************/
  86. //LCD初始化
  87. /********************************************/
  88. void LCD_init ( void )
  89. {
  90. LCD_write_com ( 0x30 ); //选择基本指令集
  91. LCD_write_com ( 0x0c ); //开显示,无游标,不反白
  92. LCD_write_com ( 0x01 ); //清除显示屏幕,把DDRAM位址计数器调整为00H
  93. Delay_nms ( 5 ); //清屏操作时间较长1.6ms 因此加此延时
  94. LCD_write_com ( 0x02 ); //清DDRAM位址归位,此处貌似与清屏重复
  95. LCD_write_com ( 0x06 ); //设定光标右移,整体显示不移动
  96. }
  97. /*************************************************/
  98. //显示字符串
  99. /*************************************************/
  100. void print ( uchar *s )
  101. {
  102. while ( *s != '\0' )
  103. {
  104. LCD_write_dat ( *s );
  105. s++;
  106. }
  107. }
  108. /***************************************************/
  109. //设置显示地址
  110. /***************************************************/
  111. void LCD_Setaddress ( uchar x, uchar y )
  112. {
  113. //地址从第1行第1列开始不从0开始
  114. uchar addr;
  115. switch ( x )
  116. {
  117. case 1:
  118. addr = 0x80 + y - 1;
  119. break;
  120. case 2:
  121. addr = 0x90 + y - 1;
  122. break;
  123. case 3:
  124. addr = 0x88 + y - 1;
  125. break;
  126. case 4:
  127. addr = 0x98 + y - 1;
  128. break;
  129. default :
  130. break;
  131. }
  132. LCD_write_com ( addr ); //字符显示开始地址
  133. }
  134. /*****************************************************/
  135. //让字符串显示在固定位置
  136. /*****************************************************/
  137. void LCD_Putstring ( uchar x, uchar y, uchar *pData )
  138. {
  139. LCD_Setaddress ( x, y );
  140. while ( *pData != '\0' )
  141. {
  142. LCD_write_dat ( *pData++ );
  143. }
  144. }
  145. /*---------------------------------------------------------------------------------------------------------------------- */
  146. //打点绘图,适用于在屏幕上打稀疏的几个点,不能用于横行连续打点
  147. void LCD_draw_point ( uchar x, uchar y )
  148. {
  149. uchar x_byte, x_bit; //在横坐标的哪一个字节,哪一个位
  150. uchar y_byte, y_bit; //在纵坐标的哪一个字节,哪一个位
  151. x_byte = x / 16; //算出它在哪一个字节(地址)
  152. //注意一个地址是16位的
  153. x_bit = x % 16; //(取模)算出它在哪一个位
  154. y_byte = y / 32; //y是没在哪个字节这个说法
  155. //这里只是确定它在上半屏(32行为一屏)还是下半屏
  156. //0:上半屏 1:下半屏
  157. y_bit = y % 32; //y_bit确定它是在第几行
  158. LCD_write_com ( 0x34 ); //打开扩展指令集
  159. LCD_write_com ( 0x80 + y_bit ); //垂直地址(上) 貌似与说明书正好相反
  160. LCD_write_com ( 0x80 + x_byte + 8 * y_byte ); //先写水平坐标(下) 貌似与说明书正好相反 ???????
  161. //具体参照数据手册
  162. //下半屏的水平坐标起始地址为0x88
  163. //(+8*y_byte)就是用来确定在上半屏还是下半屏
  164. if ( x_bit < 8 ) //如果x_bit位数小于8
  165. {
  166. LCD_write_dat ( 0x01 << ( 7 - x_bit ) ); //写高字节。因为坐标是从左向右的
  167. //而GDRAM高位在左,低位在右
  168. LCD_write_dat ( 0x00 ); //低字节全部填0
  169. }
  170. else
  171. {
  172. LCD_write_dat ( 0x00 ); //高字节全部填0
  173. LCD_write_dat ( 0x01 << ( 15 - x_bit ) );
  174. }
  175. LCD_write_com ( 0x36 ); //打开绘图显示
  176. LCD_write_com ( 0x30 ); //回到基本指令集
  177. }
  178. /************************************/
  179. //打点绘图 一次打水平一行 可以避免断点现象
  180. //x表示数组的首地址,y表示纵坐标的值,也即是表示第多少行
  181. //即对一个数组中的数进行这样的处理:
  182. //检测数组,并默认数组为一行数的记录即128字节,只要数组中有数等于y,就把第y行的数全部打出
  183. /************************************/
  184. void LCD_draw_word ( uchar *x, uchar y )
  185. {
  186. uchar i, j, k, m, n, count = 0;
  187. uchar hdat, ldat;
  188. uchar y_byte, y_bit; //在纵坐标的哪一个字节,哪一个位
  189. uchar a[16];
  190. LCD_write_com ( 0x34 ); //打开扩展指令集
  191. y_byte = y / 32; //y是没在哪个字节这个说法
  192. y_bit = y % 32; //y_bit确定它是在第几行
  193. for ( j = 0; j < 8; j++ )
  194. {
  195. hdat = 0, ldat = 0; //此处清零是很必要的
  196. n = j * 16;
  197. for ( k = n; k < n + 16; k++ )
  198. {
  199. if ( x[k] == y )
  200. {
  201. a[count] = k;
  202. count++;
  203. }
  204. }
  205. for ( m = 0; m < count; m++ )
  206. {
  207. i = a[m] - n;
  208. if ( i < 8 ) //如果x_bit位数小于8
  209. hdat = hdat | ( 0x01 << ( 7 - i ) ); //写高字节。因为坐标是从左向右的
  210. else
  211. ldat = ldat | ( 0x01 << ( 15 - i ) );
  212. }
  213. LCD_write_com ( 0x80 + y_bit ); //垂直地址(上) 貌似与说明书正好相反
  214. LCD_write_com ( 0x80 + j + 8 * y_byte ); //水平坐标(下) 貌似与说明书正好相反
  215. LCD_write_dat ( hdat );
  216. LCD_write_dat ( ldat );
  217. }
  218. LCD_write_com ( 0x36 ); //打开绘图显示
  219. LCD_write_com ( 0x30 ); //回到基本指令集
  220. }
  221. /**********************************************************/
  222. //清图形程序
  223. /**********************************************************/
  224. void LCD_draw_clr ( void )
  225. {
  226. uchar i, j;
  227. LCD_write_com ( 0x34 ); //8Bit扩充指令集,即使是36H也要写两次
  228. LCD_write_com ( 0x36 ); //绘图ON,基本指令集里面36H不能开绘图
  229. for ( i = 0; i < 32; i++ ) //12864实际为256x32
  230. {
  231. LCD_write_com ( 0x80 + i ); //行位置 貌似与说明书正好相反 (上)
  232. LCD_write_com ( 0x80 ); //列位置 貌似与说明书正好相反 (下)
  233. for ( j = 0; j < 32; j++ ) //256/8=32 byte
  234. LCD_write_dat ( 0 );
  235. }
  236. LCD_write_com ( 0x30 ); //开基本指令集
  237. }
  238. /*----------------------------------------------------------------------------------------------------------------------*/
  239. /******************************************************/
  240. //画正弦波的波形
  241. /******************************************************/
  242. void print_sinx ( void )
  243. {
  244. uchar i;
  245. uchar xdata y_sin[128]; //定义屏幕上要打的正弦波的纵坐标
  246. uchar code v[128] = {32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
  247. 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
  248. 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
  249. 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
  250. };
  251. float y;
  252. for ( i = 0; i < 128; i++ )
  253. {
  254. y = 31 * sin ( 0.09 * i ) + 0.5; //此处系数为31比较好
  255. y_sin[i] = 32 - y;
  256. }
  257. for ( i = 0; i < 64; i++ )
  258. LCD_draw_word ( y_sin, i ); //绘图 一行一行绘
  259. LCD_draw_word ( v, 32 );
  260. }
  261. /******************************************************/
  262. //主函数
  263. //用于观看显示效果
  264. /******************************************************/
  265. void main ( void )
  266. {
  267. LCD_init();
  268. LCD_Setaddress ( 2, 3 );
  269. print ( "您在使用" );
  270. LCD_Putstring ( 3, 3, a );
  271. // LCD_write_dat(0x35);
  272. LCD_draw_clr();
  273. print_sinx();
  274. while ( 1 );
  275. }