可编辑12864液晶模组,也就是液晶显示屏是有128*64个点阵组成。12864是一种图形点阵液晶显示器,它主要采用动态驱动原理由行驱动—控制器和列驱动器两部分组成了128(列)×64(行)的全点阵液晶显示此显示器采用了COB的软封装方式,通过导电橡胶和压框连接LCD,使其寿命长,连接可靠。
产品特性编辑工作电压为+5V±10%,可自带驱动LCD所需的负电压,全屏幕点阵,点阵数为128(列)×64(行),可显示8(行)×4(行)个(16×16点阵)汉字,字符16(行)x8(行)个(8 .x 8)也可完成图形字符的显示,与CPU接口采用SPI通讯方式,驱动ST7567。

ST7567芯片可写入的页有0 ~ 8 共9页,列有132列,但LCD12864只用到了8页和64列

写入数据的点阵显示效果

通信方式有并行的6800和8080,以及串行的4线方式,本次实验使用了4线串行通信,CSB为片选引脚,数据传输前拉低CSB,A0引脚说明传输的是数据还是指令,如果A0为0,说明是指令,如果为1,说明是数据,SCL为时钟线,SDA为数据/指令线,SCL为低电平时放数据或指令,SCL为高电平是ST7567读取SDA上的电平信号

LX12864官方手册(带参考代码):https://download.csdn.net/download/weixin_46251230/86731882
1、LCD初始化函数中行扫描顺序的指令是0xC0,指令说明从上到下扫描,从屏幕上看则最下面是Page0,从下往上直到Page7
2、默认的g_ucComTable数组控制页的访问顺序,默认从Page3开始,若调用 LX12864_ShowChar(0,0,‘A’);函数显示一个字符时,因为字符A高16位,所以占两页,会在Page3开始显示,到Page2结束
3、若想Page0在屏幕的最上边,则初始化时行扫描顺序指令要改为0xC8
4、如果此时g_ucComTable数组不变,则也是从Page3开始显示,再往上显示到Page2,再调用LX12864_ShowChar(0,0,‘A’);则会显示倒着的A
5、如果把g_ucComTable数组改为{0,1,2,3,4,5,6,7},若调用LX12864_ShowChar(3,0,‘A’),则字符A也会倒着显示,并且字符A的上半部分倒着显示在Page3,下半部分倒着显示在Page4,不是想要的效果
6、重新用取模软件取模字符A,则会正常显示
对参考代码进行修改:
1、初始化函数中写入0xC8指令,控制Page0是在屏幕的最上面(根据屏幕实际摆放)
2、将g_ucComTable数组改为{0,1,2,3,4,5,6,7},或者后续可以把函数中的g_ucComTable操作去掉
3、把ASCII码全部重新取模一遍,因为参考代码的字模数组要用特定的数组来显示的,通用性不太好,自己进行修改

/**
* @name gpio_Init
* @brief GPIO初始化
* @param None
* @retval None
*/
void gpio_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOB Periph clock enable */
RCC_AHBPeriphClockCmd(CLK_GPIO, ENABLE);
/*Configure GPIO_PIN*/
GPIO_InitStructure.GPIO_Pin = BL|SI|SCL|A0|RS|CS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(PORT, &GPIO_InitStructure);
GPIO_SetBits(PORT,CS);
GPIO_SetBits(PORT,SCL);
GPIO_SetBits(PORT,SI);
}
BL:背光
SI(SDA):数据/指令线
SCL:时钟线
A0:说明是数据还是指令
RS:复位
CS:片选
/**
* @name LX12864_WrCmd
* @brief 写命令
串口模式下只能写不能读,也不能查忙,因此用户要控制好速度不要太快
* @param ucCmd:命令字节
* @retval None
*/
void LX12864_WrCmd(uint8_t ucCmd)
{
uint8_t i;
GPIO_ResetBits(PORT,CS); /* CS片选引脚输出低电平 */
GPIO_ResetBits(PORT,A0); /* A0引脚输出低电平,说明是指令 */
for(i=0;i<8;i++)
{
GPIO_ResetBits(PORT,SCL);
if(ucCmd&0x80)
{
GPIO_SetBits(PORT,SI);
}
else
{
GPIO_ResetBits(PORT,SI);
}
GPIO_SetBits(PORT,SCL);
delay_us(10);
ucCmd=(ucCmd<<1);
}
GPIO_SetBits(PORT,CS);
}
/**
* @name LX12864_WrData
* @brief 写数据
* @param ucData:数据字节
* @retval None
*/
void LX12864_WrData(uint8_t ucData)
{
uint8_t i;
GPIO_ResetBits(PORT,CS); /* CS片选引脚输出低电平 */
GPIO_SetBits(PORT,A0); /* A0引脚输出高电平,说明是数据 */
for(i=0;i<8;i++)
{
GPIO_ResetBits(PORT,SCL);
if(ucData&0x80)
{
GPIO_SetBits(PORT,SI);
}
else
{
GPIO_ResetBits(PORT,SI);
}
GPIO_SetBits(PORT,SCL);
delay_us(10);
ucData=(ucData<<1);
}
GPIO_SetBits(PORT,CS);
}
/**
* @name LX12864_Init
* @brief LX12864初始化
* @param None
* @retval None
*/
void LX12864_Init(void)
{
GPIO_ResetBits(PORT,RS); /* 硬复位 */
delay_us(20);
GPIO_SetBits(PORT,RS);
delay_us(20);
LX12864_WrCmd(0xE2); /* 软复位 */
delay_us(50);
LX12864_WrCmd(0x2C); /* 升压步骤1 */
delay_us(50);
LX12864_WrCmd(0x2E); /* 升压步骤2 */
delay_us(50);
LX12864_WrCmd(0x2F); /* 升压步骤3 */
delay_us(50);
LX12864_WrCmd(0x24); /* 调整对比度,设置范围0x20~0x27 */
LX12864_WrCmd(0x81); /* 微调对比度 */
LX12864_WrCmd(0x1B); /* 微调对比度的值 */
LX12864_WrCmd(0xA3); /* 偏压比(bias),0xA2:1/9 0xA3:1=1/7 */
LX12864_WrCmd(0xA6); /* 正常显示 */
LX12864_WrCmd(0xA4); /* 全部点阵打开 */
LX12864_WrCmd(0xC8); /* 行扫描顺序:从上到下 */
LX12864_WrCmd(0xA0); /* 列扫描顺序:从左到右 */
LX12864_WrCmd(0x40); /* 起始行:第一行开始 */
LX12864_WrCmd(0xAF); /* 开显示 */
LX12864_Open_BL(); /* 显示背光 */
}

初始化命令中,设置对比度由两条指令组成,一定要先发送指令0x81,告诉ST7567接下来的一条指令是设置对比度的,再发送对比度的值,例如0x1B,由6位来控制;
设置行扫描顺序或者列扫描顺序时需要根据屏幕来设定,因为屏幕摆放的方向不同,则行列的显示也会不同
/**
* @name LX12864_FillScreen
* @brief 填充屏幕
* @param ucFillData:填充的数据
* @retval None
*/
void LX12864_FillScreen(uint8_t ucFillData)
{
uint16_t i,j;
for(i=0;i<8;i++)
{
LX12864_WrCmd(0xB0|i); /* 设置页地址 */
LX12864_WrCmd(0x10); /* 设置列地址的高位 */
LX12864_WrCmd(0x00); /* 设置列地址的低位 */
for(j=0;j<128;j++)
{
LX12864_WrData(ucFillData);
}
}
}
/**
* @name LX12864_FillPage
* @brief 填充一页
* @param ucFillData:填充的数据
* @retval None
*/
void LX12864_FillPage(uint8_t ucPage,uint8_t ucFillData)
{
uint16_t i;
LX12864_WrCmd(0xB0|ucPage); /* 设置页地址 */
LX12864_WrCmd(0x10); /* 设置列地址的高位 */
LX12864_WrCmd(0x00); /* 设置列地址的低位 */
for(i=0;i<128;i++)
{
LX12864_WrData(ucFillData);
}
}
/**
* @name LX12864_ShowHorLine
* @brief 显示一条横线
* @param ucPage:页地址 范围:0 ~ 7
* @param ucCol:列地址 范围:0 ~ 127
* @param ucLen:长度 范围:0 ~ 127
* @param ucData:数据
* @retval None
*/
void LX12864_ShowHorLine(uint8_t ucPage,
uint8_t ucCol,
uint8_t ucLen,
uint8_t ucData)
{
uint8_t i;
LX12864_WrCmd((ucPage&0x07)|0xB0); /* 设置页地址 */
LX12864_WrCmd((ucCol>>4)|0x10); /* 设置列地址高位 */
LX12864_WrCmd(ucCol&0x0F); /* 设置列地址低位 */
for(i=0;i<ucLen;i++)
{
LX12864_WrData(ucData);
}
}
/**
* @name LX12864_ShowVerLine
* @brief 显示一条竖线
* @param ucStartPage:起始页地址 范围:0 ~ 7
* @param ucCol:列地址
* @param ucEndPage:结束页地址 范围:0 ~ 7 注意:结束页地址要大于或等于起始页地址
* @retval None
*/
void LX12864_ShowVerLine(uint8_t ucStartPage,
uint8_t ucCol,
uint8_t ucEndPage)
{
uint8_t i,ucTemp;
ucTemp = (ucEndPage - ucStartPage)+1;
for(i=0;i<ucTemp;i++)
{
LX12864_WrCmd((ucStartPage&0x07)|0xB0); /* 设置页地址 */
LX12864_WrCmd((ucCol>>4)|0x10); /* 设置列地址高位 */
LX12864_WrCmd(ucCol&0x0F); /* 设置列地址低位 */
LX12864_WrData(0xFF);
ucStartPage++;
}
}
typedef enum
{
g_enBigNumber,
g_enSmallNumber,
}__ENCHARARRAY_T;
/**
* @name LX12864_ShowChar
* @brief 显示字符
* @param ucPage:页地址 范围:0 ~ 7
* @param ucCol:列地址 范围:0 ~ 127
* @param ucWidth:字体宽度
* @param ucHight:字体高度
* @param ucChar:字符
* @param ucCharArray:字模数组
* @retval None
*/
void LX12864_ShowChar(uint8_t ucPage,
uint8_t ucCol,
uint8_t ucWidth,
uint8_t ucHight,
uint8_t ucChar,
__ENCHARARRAY_T ucCharArray)
{
uint8_t i,j,ucNeedPage;
uint16_t ucIndex = 0;
ucNeedPage = ucHight / 8;
if((ucHight%8) != 0)
{
ucNeedPage = ucNeedPage+1; /* 加上不够一页的部分 */
}
for(i=0;i<ucNeedPage;i++)
{
LX12864_WrCmd((ucPage&0x07)|0xB0); /* 设置页地址 */
LX12864_WrCmd((ucCol>>4)|0x10); /* 设置列地址高位 */
LX12864_WrCmd(ucCol&0x0F); /* 设置列地址低位 */
for(j=0;j<ucWidth;j++)
{
if(ucCharArray == g_enBigNumber)
{
LX12864_WrData(g_ucBigNumber[ucChar][ucIndex]); /* 大字体字模显示字符 */
ucIndex++;
}
else if(ucCharArray == g_enSmallNumber)
{
LX12864_WrData(g_ucSmallNumber[ucChar][ucIndex]); /* 小字体字模显示字符 */
ucIndex++;
}
}
ucPage++;
}
}
设置页地址时,参数ucPage为想要显示字符的页数,因为页被分为了0 ~ 7页, 一共8页,只用3位二进制位即可遍历8页,28 = 3,所以&0x07是为了取出ucPage的低3位,再或上0xB0,就设置了想要显示的页
/* 小字体的字模数组 */
unsigned char g_ucSmallNumber[][24]={
{0xC0,0xE0,0x70,0x30,0x30,0x70,0xE0,0xC0,0x3F,0x7F,0xE0,0xC0,0xC0,0xE0,0x7F,0x3F,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"0",0*/
/* (8 X 17 , Arial, 加粗 )*/
{0x00,0x80,0xC0,0x60,0xF0,0xF0,0x00,0x00,0x00,0x01,0x00,0x00,0xFF,0xFF,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"1",1*/
/* (8 X 17 , Arial, 加粗 )*/
{0xC0,0xE0,0x70,0x30,0x30,0x30,0xE0,0xC0,0xC0,0xE0,0xF0,0xD8,0xDC,0xCE,0xC7,0xC1,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"2",2*/
…………………………
};
用PCtoLCD2002取模软件取出想要显示的字符,本次实验用二维数组保存字模数据,根据参数ucChar作为下标去找到要显示的字符的一位数组,再遍历数组写入数据
/**
* @name Pow
* @brief 求sBase的sPower次方
* @param sBase:底数
* @param sPower:次方数
* @retval 返回次方后的结果
*/
int16_t Pow(int16_t sBase,int16_t sPower)
{
uint8_t i;
int16_t sResult = 1;
for(i = 0; i < sPower; i++)
{
sResult *= sBase;
}
return sResult;
}
/**
* @name LX12864_ShowVariableNum
* @brief 显示有符号的整型数
* @param ucPage:页地址,范围:0 ~ 7
* @param ucCol:列地址,范围:0 ~ 127
* @param ucWidth:字符宽度
* @param ucHight:字符高度
* @param ucNumLen:待显示数字的位数
* @param sNum:待显示的数字
* @param ucCharArray:字模数组
* @retval None
*/
void LX12864_ShowSignedNum(uint8_t ucPage,
uint8_t ucCol,
uint8_t ucWidth,
uint8_t ucHight,
uint8_t ucNumLen,
int16_t sNum,
__ENCHARARRAY_T ucCharArray)
{
uint8_t i;
int16_t sNumTemp,sResult;
if(sNum >= 0)
{
LX12864_ShowChar(ucPage,ucCol-(ucWidth+2),ucWidth,ucHight,11,ucCharArray); /* 11对应空白,如果是正数则去掉负号 */
sNumTemp = sNum;
}
else
{
LX12864_ShowChar(ucPage,ucCol-(ucWidth+2),ucWidth,ucHight,10,ucCharArray); /* 10对应字符'-',负数则在前面加上负号 */
sNumTemp = -sNum;
}
for(i=ucNumLen;i>0;i--)
{
sResult = sNumTemp/Pow(10,i-1)%10; /* 从高位开始,取出数字的每一位 */
LX12864_ShowChar(ucPage,ucCol+((ucWidth+2)*(ucNumLen-i)),ucWidth,ucHight,sResult,ucCharArray);/*显示数字 */
}
}
其中Pow函数的用法参考了之前写的LCD1602代码,可以参考
http://t.csdn.cn/mMBIA
图形需要用取模软件生成
/**
* @name LX12864_ShowBmp
* @brief 显示图形
* @param ucPage:页地址 范围:0 ~ 7
* @param ucCol:列地址 范围:0 ~ 127
* @param ucWidth:图形宽度
* @param ucHight:图形高度
* @param pucBmp:图形字模数组指针
* @retval None
*/
void LX12864_ShowBmp(uint8_t ucPage,
uint8_t ucCol,
uint8_t ucWidth,
uint8_t ucHight,
uint8_t* pucBmp)
{
uint8_t i,j,ucNeedPage;
uint16_t ucIndex = 0;
ucNeedPage = ucHight / 8; /* 所需页数 */
if((ucHight%8) != 0)
{
ucNeedPage = ucNeedPage+1; /* 加上不够一页的部分 */
}
for(i=0;i<ucNeedPage;i++)
{
LX12864_WrCmd((ucPage&0x07)|0xB0); /* 设置页地址 */
LX12864_WrCmd((ucCol>>4)|0x10); /* 设置列地址高位 */
LX12864_WrCmd(ucCol&0x0F); /* 设置列地址低位 */
for(j=0;j<ucWidth;j++)
{
LX12864_WrData(pucBmp[ucIndex]); /* 显示图形 */
ucIndex++;
}
ucPage++;
}
}
实现亮一会,暗一会主要通过修改对比度来实现
在LCD_Init初始化函数中,有对比度的设置
LX12864_WrCmd(0x81); /* 微调对比度 */
LX12864_WrCmd(0x1B); /* 微调对比度的值 */

要先写入指令0x81,说明是后续的一个字节是修改对比度的
后续这个字节的是6位的,EV5和EV4的值会影响屏幕显示,有下面三种情况
0x1B:
正常显示(屏幕有点坏)

0x2B:
会显示填充整个128x64屏幕的黑点,“腾讯QQ”显示会比较暗,正面看看不清

如果从侧面看则可以看清

0x3B:
正面看看不到显示内容,屏幕全填充

侧面看也有黑点

所以调整对比度就只能设置EV3 ~ EV0这4位,高两位的EV4设置为1,EV5设置为0,才能正常显示
经过测试,0x1F对比度最高,显示内容很清晰,值越小则对比度越低,显示内容逐渐变暗,0x10时几乎看不见显示的内容
在主函数循环中,通过改变对比度的值,再加上适当的延时,就可以让显示内容亮一会,暗一会
/*
* @name Run
* @brief 系统运行
* @param None
* @retval None
*/
void Run(void)
{
LX12864_WrCmd(0x81); /* 微调对比度 */
LX12864_WrCmd(0x1B); /* 亮 */
LCD_ShowBmp(g_ucBmp1);
delay_ms(2000);
delay_ms(2000);
LX12864_WrCmd(0x81); /* 微调对比度 */
LX12864_WrCmd(0x15); /* 微亮 */
LCD_ShowBmp(g_ucBmp1);
delay_ms(2000);
delay_ms(2000);
}

右上角的WiFi显示一会亮,一会灭
实现思路:因为改变对比度是对一整个屏幕而言的,没发现有对某一部分屏幕改变对比度的方法,所以对WiFi显示的字模数组全部改为0x00,则WiFi图形这一部分就显示空白,WiFi图形显示1秒后再写入全是0x00的字模数组,则可以对WiFi图形擦除,擦除1秒后再显示,则可以达到闪烁的目标
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道rubyonrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
我使用的是最新版本的Chrome(32.0.1700.107)和Chrome驱动程序(V2.8)。但是当我在Ruby中使用以下代码运行示例测试时:require'selenium-webdriver'WAIT=Selenium::WebDriver::Wait.new(timeout:100)$driver=Selenium::WebDriver.for:chrome$driver.manage.window.maximize$driver.navigate.to'https://www.google.co.in'defapps_hoverele_hover=$driver.find_
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。多年来,我一直在使用多种语言进行编程,并且认为自己总体上相当擅长。但是,我从未编写过任何自动化测试:没有单元测试,没有TDD,没有BDD,什么都没有。我已经尝试开始为我的项目编写适当的测试套件。我可以看到在进行任何更改后能够自动测试项目中所有代码的理论值(value)。我可以看到像RSpec和Mocha这样的测试框架应该如何使设置和运行所述测试变得相当容易
如果我在功能规范中调用url_for,它会返回一个以http://www.example.com/开头的绝对URL.Capybara会很乐意尝试加载该站点上的页面,但这与我的应用程序无关。以下是重现该问题的最少步骤:从这个Gemfile开始:source'https://rubygems.org'gem"sqlite3"gem"jquery-rails"gem"draper"gem"rails",'4.1.0'gem"therubyracer"gem"uglifier"gem"rspec-rails"gem"capybara"gem"poltergeist"gem"launchy"运行
在笔者前面有一篇文章《驱动开发:断链隐藏驱动程序自身》通过摘除驱动的链表实现了断链隐藏自身的目的,但此方法恢复时会触发PG会蓝屏,偶然间在网上找到了一个作者介绍的一种方法,觉得有必要详细分析一下他是如何实现的进程隐藏的,总体来说作者的思路是最终寻找到MiProcessLoaderEntry的入口地址,该函数的作用是将驱动信息加入链表和移除链表,运用这个函数即可动态处理驱动的添加和移除问题。MiProcessLoaderEntry(pDriverObject->DriverSection,1)添加MiProcessLoaderEntry(pDriverObject->DriverSection,
我有一个适用于事件/监听器模型的应用程序。发布了几种不同类型的数据(事件),然后许多不同的事情可能需要也可能不需要对该数据(监听器)采取行动。监听器的发生没有特定的顺序,每个监听器将决定是否需要对事件采取行动。Rails应用程序有哪些工具可以完成此任务?我希望自己不必这样做(尽管我可以。这没什么大不了的。)编辑:观察者模式可能是更好的选择 最佳答案 查看EventMachine.它是一个非常流行的Ruby事件处理库。它看起来相当不错,而且很多其他库似乎都在利用它(Cramp)。这是一个很好的介绍:http://rubylearnin
什么是最优雅的做法'string'=>['s','st','str','stri','strin','string']我一直在想一个类轮,但我不能完全做到。欢迎任何解决方案,谢谢。 最佳答案 这个怎么样?s='string'res=s.length.times.map{|len|s[0..len]}res#=>["s","st","str","stri","strin","string"] 关于ruby-'string'到['s','st','str','stri','strin','s
一、概述在之前的一篇博文中,记录了AT24C01、AT24C02芯片的读写驱动,先将之前的相关文章include一下:1.IIC驱动:4位数码管显示模块TM1637芯片C语言驱动程序2.AT24C01/AT24C02读写:AT24C01/AT24C02系列EEPROM芯片单片机读写驱动程序本文记录分享AT24C04、AT24C08、AT24C16芯片的单片机C语言读写驱动程序。二、芯片对比介绍型号容量bit容量byte页数字节/页器件寻址位可寻址器件数WordAddress位数/字节数备注AT24C044k5123216A2A149/1WordAddress使用P0位AT24C088k1024
首先,DateTime格式变量似乎没有在任何地方记录,因此对可以在rubydocs中向我展示此内容的任何人+1。其次,在查看Date.strftime函数代码时,我没有看到任何可以让我执行以下操作的内容:2010年9月9日,星期四有人知道这是否可行吗? 最佳答案 您可能想要takealookhere.总结time=DateTime.nowtime.strftime("%A,%B#{time.day.ordinalize}%Y")请注意,您在纯Ruby(2.0)中运行,您需要调用:require'active_support/core