文章最后附源代码链接
SSD1306单片机接口由8个数据引脚和5个控制引脚组成。不同接口模式下的引脚分配如表所示。在BS[2:0]引脚上通过硬件选择可设置不同的MCU模式。
通常我们所用的OLED屏有白色、蓝色、黄蓝双色等几种;屏的大小为0.96寸,像素点为128*64,所以我们也称之为0.96OLED屏或者12864屏。
内部驱动IC为SSD1306;通信方式一般为SPI或者I2C。如下图所示,配置哪种模式主要是根据BS0、BS1和BS2这三个管脚的电平逻辑来的。
①IIC模式:
电阻焊接R1、R4、R6、R7、R8。D1作为SCK时钟线,D0作为SDA数据线。SSD1306作为从机地址为0x78,DC脚作为更改从机地址引脚,DC接VCC,从机地址0x79,什么都不接地址为0x78。
发送数据:起始信号-从机地址-应答-写数据模式(0x40)-应答-数据(8bit)-结束信号
发送命令:起始信号-从机地址-应答-写命令模式(0x00)-应答-命令(8bit)-结束型号
②四线SPI模式:
电阻焊接R3、R4。D1作为SClk时钟线,D0作为MOSI线,CS为片选NSS,DC为数据/命令线、RES复位线。
发送数据:CS拉低-DC拉高-发送数据(8bit)-CS拉高
发送命令:CS拉低-DC拉低-发送命令(8bit)-CS拉高
③三线SPI模式:
电阻焊接R2,R3。D1作为SCLK时钟线,D0作为MOSI线,CS片选NSS,RES复位,DC拉低不用。数据有9位,首位就是数据/命令,因此不用DC线。
发送数据:CS拉低-首位为1再接着发送8位数据-CS拉高
发送命令:CS拉低-首位为0再接着发送8位命令-CS拉高
OLED的显存分布情况。我们可以理解为:水平方向分布了128个像素点,垂直方向分布了64个像素点。而驱动芯片在点亮像素点的时候,是以8个像素点为单位的。官方的例程推荐的是垂直扫描的方式,也就是先画垂直方向的8个像素点(如下图所示),所以我们在画点的时候Y的取值为0-7,X的取值为0-127。页是芯片设计者为了方便将同一列的8个点阵编成一组,用一个8bit数表示,这样的8行128个数被称为1页。
选择阴码、行列式(搭配页寻址模式)、逆向
①单级命令:直接发送一个命令就能设置OLED屏。
OLED_WriteCmd(0xAE); //关闭显示OLED_WriteCmd(0xA4); //设置整个显示打开/关闭OLED_WriteCmd(0xAF); //开启显示OLED_WriteCmd(0xA4); //设置整个显示打开/关闭
②多级命令:先发送一个命令,紧接着发送该命令的参数
OLED_WriteCmd(0x20); //打开寻址模式选择OLED_WriteCmd(0x02); //寻址模式选择页寻址模式
寻址模式命令:0x20
模式 | 命令 |
水平寻址模式 | 0x00 |
垂直寻址模式 | 0x01 |
页寻址模式 | 0x02 |
①页寻址模式(默认模式):
从设定的页和列开始发送数据,列地址自动累加,页地址不会更新,如果超出范围则超出部分无效,可以不用事先指定每页中每列的长度。
地址设置:
y是页地址的编号:
使用或命令将这低四位与固定前缀0xb连接起来(0xb0 | y)
x是列地址的编号:
发送高四位的程序,先使用与命令(&)取得x的高4位(x & 0xf0)之后向右移4位,将高4位数据放到低4位((x & 0xf0) >> 4)之后使用或命令(|)加上固定前缀0x1即可(((x & 0xf0) >> 4) | 0x10)
发送低四位命令因为地址低四位的高四位为0x0,因此只需要使用与命令(&)将高四位置零即可(x & 0x0f)
②水平寻址:
横向编码,列地址自动加,遇到设置范围的页尾时自动跳转到下一页,传输到设置范围的最后一页最后一列时自动复位。
地址设置:
OLED_WriteCmd(0x20) //寻址模式选择:(0x00水平/0x01垂直/0x02页)OLED_WriteCmd(0x00) //水平寻址模式OLED_WriteCmd(0x21) //设置列地址OLED_WriteCmd(0x00) //设置列起始位置OLED_WriteCmd(0x7F) //设置列终止位置OLED_WriteCmd(0x22) //设置页地址OLED_WriteCmd(0x00) //设置页起始位置OLED_WriteCmd(0x07) //设置页终止位置
③垂直寻址:
在垂直寻址模式下,当对显示内存进行读写操作后,页面地址指针自动加1。如果页地址指针到达页结束地址,则页地址指针重置为页开始地址,并且列地址指针加1。垂直寻址模式下页和列地址点的移动顺序如图所示。当列地址和页地址指针都到达结束地址时,指针被重定向到列的开始地址和通道的开始地址。
地址设置:
OLED_WriteCmd(0x20) //寻址模式选择:(0x00水平/0x01垂直/0x02页)OLED_WriteCmd(0x01) //垂直寻址模式OLED_WriteCmd(0x21) //设置列地址OLED_WriteCmd(0x00) //设置列起始位置OLED_WriteCmd(0x7F) //设置列终止位置OLED_WriteCmd(0x22) //设置页地址OLED_WriteCmd(0x00) //设置页起始位置OLED_WriteCmd(0x07) //设置页终止位置
①对比度设置命令
进入对比度设置模式:0x81
之后输入对比度数值范围是0x00 - 0xFF(对比度实际上在这里就是设置发光部分的亮度,数值越大,亮度越大)
②显示开启/关闭
显示开启:0xA4
显示关闭:0xA5
③显示模式
阳码显示:0xA6
阴码显示:0xA7
④显示控制命令
关闭显示:0xAE
开启显示:0xAF
关闭滚动:0x2E(设置滚动参数前要先关闭滚动,防止RAM错乱)
开启滚动:0x2F
向右滚动:0x29
向左滚动:0x2a
A-F滚动参数配置,5个字节配置参数。
①设置显示开始行
默认起始行:0x40
②设置列重映射(就是设置是否水平镜像)
将列地址0映射到SEG0:0xA0
将列地址127映射到SEG0:0xA1
设置左右方向,0xA1正常 0xA0左右反置
③设置多路复用率
开启多路复用率:0xA8
参数:A[5:0] 0到63配置参数。
④设置COM输出扫描方向
设置上下方向,0xC8正常 0xC0上下反置
⑤设置显示偏移量
开启显示偏移量:0xD3
参数:A[5:0] 垂直偏移量0-63
⑥设置COM引脚硬件配置
开启COM应交硬件配置;0xDA
参数:
A[4]=0b,顺序COM引脚配置
[4]=1b(RESET),可选COM引脚配置
A[5]=0b(RESET),禁用COM左右重映射
A[5]=1b,启用COM左右重映射
①设置显示时钟分频率/振荡器频率
开启命令:0xD5
参数A[7:0]:
A[3:0]:定义显示时钟(DCLK)的分割比(D):除比= A[3:0] + 1, RESET为0000b(除比= 1)
A[7:4]:设置振荡器频率,FOSC。振荡器频率随着A[7:4]的值增加,反之亦然。RESET为1000b范围:0000 ~ 1111 b频率随设定值增加而增加。
②设置预充电周期
开启命令:0xD9
参数A[7:0]:
A[3:0]:阶段1周期最多15个DCLK时钟,0是无效的条目(重置= 2h)
A[7:4]:阶段2周期最多15个DCLK时钟,0是无效的条目(重置= 2h)
③设置VCOMH取消选择级别
开启命令:0xDB
参数A[6:4]
④无操作命令
开启无操作:0xE3
/*引脚配置*/
#define OLED_W_D0(x) GPIO_WriteBit(GPIOB, GPIO_Pin_12, (BitAction)(x))
#define OLED_W_D1(x) GPIO_WriteBit(GPIOB, GPIO_Pin_13, (BitAction)(x))
#define OLED_W_RES(x) GPIO_WriteBit(GPIOB, GPIO_Pin_14, (BitAction)(x))
#define OLED_W_DC(x) GPIO_WriteBit(GPIOB, GPIO_Pin_15, (BitAction)(x))
#define OLED_W_CS(x) GPIO_WriteBit(GPIOA, GPIO_Pin_8, (BitAction)(x))
/*引脚初始化*/
void OLED_SPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOA, &GPIO_InitStructure);
OLED_W_D0(1);
OLED_W_D1(1);
OLED_W_RES(1);
OLED_W_DC(1);
OLED_W_CS(1);
}
void OLED_Init(void)
{
HAL_Delay(5);
SPI_Init();
OLED_WriteCmd(0xAE); //关闭显示
OLED_WriteCmd(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCmd(0x80);
OLED_WriteCmd(0xA8); //设置多路复用率
OLED_WriteCmd(0x3F);
OLED_WriteCmd(0xD3); //设置显示偏移
OLED_WriteCmd(0x00);
OLED_WriteCmd(0x40); //设置显示开始行
OLED_WriteCmd(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCmd(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCmd(0xDA); //设置COM引脚硬件配置
OLED_WriteCmd(0x12);
OLED_WriteCmd(0x81); //设置对比度控制
OLED_WriteCmd(0xCF);
OLED_WriteCmd(0xD9); //设置预充电周期
OLED_WriteCmd(0xF1);
OLED_WriteCmd(0xDB); //设置VCOMH取消选择级别
OLED_WriteCmd(0x30);
OLED_WriteCmd(0xA4); //设置整个显示打开/关闭
OLED_WriteCmd(0xA6); //设置正常/倒转显示
OLED_WriteCmd(0x8D); //设置充电泵
OLED_WriteCmd(0x14);
OLED_WriteCmd(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
/**
* @brief SPI发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_SPI_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_D0(0);
OLED_W_D1(Byte & (0x80 >> i));
OLED_W_D0(1);
}
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCmd(uint8_t Command)
{
OLED_W_CS(0);
OLED_W_DC(0);
OLED_SPI_SendByte(Command);
OLED_W_CS(1);
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_W_CS(0);
OLED_W_DC(1);
OLED_SPI_SendByte(Data);
OLED_W_CS(1);
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCmd(0xB0 | Y); //设置Y位置
OLED_WriteCmd(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCmd(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED显示汉字
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param No:要显示汉字的个数
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowChinese(uint8_t Line,uint8_t Column,uint8_t No)
{
uint8_t i;
OLED_SetCursor((Line-1)*2,(Column-1)*16);
for(i=0;i<16;i++)
{
OLED_WriteData(Chinese[2*No][i]);
}
OLED_SetCursor((Line-1)*2+1,(Column-1)*16);
for(i=0;i<16;i++)
{
OLED_WriteData(Chinese[2*No+1][i]);
}
}
/**
* @brief OLED显示图像
* @param Row1 起始行位置,范围:0~8
* @param Column1 起始列位置,范围:0~128
* @param Row2 终止行位置,范围:0~8
* @param Column2 终止列位置,范围:0~128
* @param BMP1:要显示gif的数组
* @retval 无
*/
void OLED_ShowBMP(uint8_t Row1, uint8_t Column1,uint8_t Row2, uint8_t Column2,uint8_t BMP1[])
{
unsigned int i=0;
unsigned char x,y;
// if(y1%8==0) y=y1/8;
// else y=y1/8+1;
for(x=Row1;x<Row2;x++)
{
OLED_SetCursor(x,Column1);
for(y=Column1;y<Column2;y++)
{
OLED_WriteData(BMP1[i++]);
}
}
}
void OLED_CloseRoll(void)
{
//以下添加水平滚动效果
OLED_WriteCmd(0x2e);//关滚动
OLED_WriteCmd(0x2a);//29向右,2a向左
OLED_WriteCmd(0x00);//A:空字节
OLED_WriteCmd(0x00);//B:水平起始页
OLED_WriteCmd(0x07);//C:水平滚动速度
OLED_WriteCmd(0x07);//D:水平结束页
OLED_WriteCmd(0x00);//E:每次垂直滚动位移
// OLED_WriteCmd(0x2f);//开滚动
}
void OLED_OpenRoll(void)
{
//以下添加水平滚动效果
OLED_WriteCmd(0x2f);//开滚动
}
链接:https://pan.baidu.com/s/1xlNhgYBokVcr8njH9ntNjQ
提取码:xiao
我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择
我试图在索引页中创建一个超链接,但它没有显示,也没有给出任何错误。这是我的index.html.erb代码。ListingarticlesTitleTextssss我检查了我的路线,我认为它们也没有问题。PrefixVerbURIPatternController#Actionwelcome_indexGET/welcome/index(.:format)welcome#indexarticlesGET/articles(.:format)articles#indexPOST/articles(.:format)articles#createnew_articleGET/article
我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c
目前,Itembelongs_toCompany和has_manyItemVariants。我正在尝试使用嵌套的fields_for通过Item表单添加ItemVariant字段,但是使用:item_variants不显示该表单。只有当我使用单数时才会显示。我检查了我的关联,它们似乎是正确的,这可能与嵌套在公司下的项目有关,还是我遗漏了其他东西?提前致谢。注意:下面的代码片段中省略了不相关的代码。编辑:不知道这是否相关,但我正在使用CanCan进行身份验证。routes.rbresources:companiesdoresources:itemsenditem.rbclassItemi
SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手
如果我在模型中设置验证消息validates:name,:presence=>{:message=>'Thenamecantbeblank.'}我如何让该消息显示在闪光警报中,这是我迄今为止尝试过的方法defcreate@message=Message.new(params[:message])if@message.valid?ContactMailer.send_mail(@message).deliverredirect_to(root_path,:notice=>"Thanksforyourmessage,Iwillbeintouchsoon")elseflash[:error]
我刚刚按照thebootsygempage上的安装说明进行操作在我保存并查看帖子内容之前,一切看起来都不错。这是输出在View中的样子:HeaderSubhead:似乎没有呈现任何html格式,因为它被引号或类似的东西转义了-其他人有这个问题吗?我没有在github页面或SO上看到任何问题来指出我正确的方向。除了遵循gem安装说明之外,我还没有做任何事情,但也许我错过了什么或者只是犯了一个愚蠢的错误。如果你还有什么想知道的,请尽管问。干杯 最佳答案 你需要有这样的东西,转义html: 关
我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认