作者:titer1
联系:1307316一九六八(仅接受短信)
声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处。
本文所有代码版权归原作者所有
目录
2440系统如何使用spi的
tips:同步后 ,ctrl+ alt +enter 预览
当arm读取spi设备时,
这是 spi transfer format细节,
送完一个字节,下一个时钟周期没有到来之前,di内容存在差异
详细如图:
韦大大说,时钟周末没来之前的事情 不用在乎,在乎时钟边缘
还有时钟的极性差别,如下图橙色标示:
上图我看来,就是时钟开始时电平的高/低
至于如何选定正确的极性作为开始,要看外接芯片的约定
结合 SPEC UG-2864TMBEG01 手册,可以看到图中时钟起始选择可高可低,不用担心
韦大大说,arm spi format a/b
不过外接的spi芯片 DO数据是在上升沿由外接芯片锁存住。
那么 ,最终arm 2440可选的format格式剩下为;
以上场景区分是 arm的 cpol /cpha的值
下面开始代码了
当前是 spi第一课第一节,修改代码在 spi_i2c_adc里面
一个文件负责 显示,
一个文件负责传输
目标实现函数有:
SPIInit();
OLEDInit();
OLEDPrint();
主要分析内容来源是:源码source\裸板\01th_spi_i2c_adc_jz2440_oled
简单来说就是OLED开头的函数实现,在这里。
从芯片SPEC UG-2864TMBEG01 手册看来,vcc产生是 内部dc上拉结果?
改装芯片手册内部的初始化设置
详细的cpu和芯片的连线图如下,这次跟之前给出的连线图差别在,给出data/控制信号切换的方法
static void OLEDWriteCmd(unsigned char cmd) {
OLED_Set_DC(0); /* command */
OLED_Set_CS(0); /* select OLED */
SPISendByte(cmd);
OLED_Set_CS(1); /* de-select OLED */
OLED_Set_DC(1); /* */
}
//入口函数
void OLEDInit(void) {
/* 向OLED发命令以初始化 */
OLEDWriteCmd(0xAE); /*display off*/
...
OLEDWriteCmd(0x14);
...
}
继续看 oled控制器 (?)的手册 SSD1306-Revision 1.1 (Charge Pump)
这里 dc对应gpg4 ,cs对应gpf1
static void OLED_Set_DC(char val) {
if (val)
GPGDAT |= (1<<4);
else
GPGDAT &= ~(1<<4);
}
static void OLED_Set_CS(char val) {
if (val)
GPFDAT |= (1<<1);
else
GPFDAT &= ~(1<<1);
}
###实现 Oledprint
关键在 oled 地址设定+ 数据设定
这里采用的取地址方式是:页寻址
显存这里大小为:128*64
一行64字节,分为8页,每页8行
手册解释 详细看
10 COMMAND DESCRIPTIONS
10.1 Fundamental Command
10.1.1 Set Lower Column Start Address for Page Addressing Mode (00h~0Fh)
10.1.2 Set Higher Column Start Address for Page Addressing Mode (10h~1Fh)
10.1.3 Set Memory Addressing Mode (20h)
There are 3 different memory addressing mode in SSD1306: page addressing mode,
horizontal addressing mode and vertical addressing mode.
情景如图:
中文总结如下:
看手册
看最终屏幕上的布局,这里就可以理解为什么要两行了,下面说明相关代码
//每一个字节的字模数据都分解为高4位+低4位传输
//之所以相加:因为是bit 0:1 ,后面处理了高位和低位
static void OLEDSetPos(int page, int col) {
OLEDWriteCmd(0xB0 + page); /* page address */
OLEDWriteCmd(col & 0xf); /* Lower Column Start Address */
OLEDWriteCmd(0x10 + (col >> 4)); /* Lower Higher Start Address */
}
下面的函数OLEDPutChar形象的说明了绘制字模的过程。
//涵盖字符转换为字模
/* page: 0-7 * col : 0-127 * 字符: 8x16象素 */
void OLEDPutChar(int page, int col, char c) {
int i = 0;
/* 得到字模 */
const unsigned char *dots = oled_asc2_8x16[c - ' '];
/* 发给OLED */
OLEDSetPos(page, col);
/* 发出8字节数据 */
for (i = 0; i < 8; i++)
OLEDWriteDat(dots[i]);
OLEDSetPos(page+1, col);
/* 发出8字节数据 */
for (i = 0; i < 8; i++)
OLEDWriteDat(dots[i+8]);
}
//处理的对象是我们肉眼看到的字符
/* page: 0-7 * col : 0-127 * 字符: 8x16象素 */
void OLEDPrint(int page, int col, char *str) {
int i = 0;
while (str[i])
{
OLEDPutChar(page, col, str[i]);
col += 8;
if (col > 127)
{
col = 0;
page += 2;
}
i++;
}
}
oled设置page模式
oled清屏函数
此处暂时忽略。
至此:字符–》字模点阵–》spi 数据传输的过程就通过以上看到
OLEDPrint -->OLEDPutChar -->( OLEDWriteDat-->OLEDWriteCmd)
cloc对应gpg7 do对应gpd6,非常有趣重要的是上升沿的do实现
SPI_Set_CLK(0);
SPI_Set_DO(val & 0x80);//典型取高位
SPI_Set_CLK(1);
完整的SPISendByte逻辑在下面
static void SPI_Set_CLK(char val) {
if (val)
GPGDAT |= (1<<7);
else
GPGDAT &= ~(1<<7);
}
static void SPI_Set_DO(char val) {
if (val)
GPGDAT |= (1<<6);
else
GPGDAT &= ~(1<<6);
}
void SPISendByte(unsigned char val) {
int i;
for (i = 0; i < 8; i++)
{
SPI_Set_CLK(0);
SPI_Set_DO(val & 0x80);
SPI_Set_CLK(1);
val <<= 1;
}
}
首先看spi芯片的电路图
然后使用gpio模拟spi,尤其注意里面的注释,这里比较细节就是gpio方向
/* 用GPIO模拟SPI */
//called by SPIInit
static void SPI_GPIO_Init(void) {
/* GPF1 OLED_CSn output */
GPFCON &= ~(3<<(1*2));
GPFCON |= (1<<(1*2));
GPFDAT |= (1<<1); //设置为高电平,保证片选引脚pin1(GPF1 OLED_CSn)和其他相关引脚不同时为0,
/* GPG2 FLASH_CSn output * GPG4 OLED_DC output * GPG5 SPIMISO input * GPG6 SPIMOSI output * GPG7 SPICLK output */
GPGCON &= ~((3<<(2*2)) | (3<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2)));
GPGCON |= ((1<<(2*2)) | (1<<(4*2)) | (1<<(6*2)) | (1<<(7*2)));
GPGDAT |= (1<<2);//设置为高电平,保证片选引脚pin2(GPG2 FLASH_CSn) 和其他相关引脚不同时为0,
}
当前进度是在 1-1 30分钟位置
作者所使用的代码在 spi_i2c_adc相关文件夹中