SPI简介
SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议,比如AT91RM9200。
简单的说,就是SPI是一种告诉的协议,相当于IIC,只是IIC是两根线(时钟线SCL,数据地址线SDA)而SPI是四根线(SCK时钟信号,DO输出线,DI输入线,CSn片选信号),同样可以挂在多个SPI高速设备。
数据传输
数据传输由CPOL,CPHA两个控制器决定,CPOL决定电平启示状态是低电平还是高电平,CPHA决定在第一个沿采样还是第二个沿采样
通过SPI协议实现对OLED屏幕的显示
OLED显示原理
屏幕分辨率是12864,如同LCD一样,我们需要一个显存来存放数据,这个显存在内存中开辟,一个字节八位,分别表示该列元素的亮(1)或者灭(0)
按168的大小在屏幕上显示,一行为一个page,在光标移动到底8列时,光标归为0,page+1,实现第二个page的写操作
通过GPIO模拟SPI的数据收发
原理图中使用到GPG3567和GPF1,所以需要设置S3C2440相应的管脚的输入输出模式,除开GPG5为输入模式,其余管脚都是输出模式。
static void SPI_GPIO_INIT(void)
{
/*GPF1 OLED_CSn OUTPUT*/ /*主机是输出模式*/
GPFCON &= ~(3<<2);
GPFCON |= (1<<2);
GPFDAT |= (1<<1); /*因为接了OLED 和 FLASH 如果同时为0 会产生冲突*/
/*GPG2 FLASH_CSn OUTPUT*/
/*GPG4 OLED_DC OUTPUT*/
/*GPG5 SPIMISO INPUT*/ /*MI:MASTER INPUT*/
/*GPG6 SPIMOSI OUTPUT*/
/*GPG7 SPICLK OUTPUT*/
GPGCON &= ~((3<<4)|(3<<8)|(3<<12)|(3<<14)|(3<<10));
GPGCON |= ((1<<4)|(1<<8)|(1<<12)|(1<<14));
GPGDAT |=(1<<2); /*因为接了OLED 和 FLASH 如果同时为0 会产生冲突*/
/*CSn低电平有效*/
/*都不片选,使其在使用时裁片选*/
}
由于芯片上接入了FLASH和oled的片选信号,所以在GPIO的初始化时,需要将两个片选信号都拉高,避免默认状态冲突。
OLED初始化
void oledinit(void)
{
/*向OLED发命令初始化*/
/*SPEC UG手册提供的初始化命令*/
OLEDWriteCmd(0xAE); /*display off*/
OLEDWriteCmd(0x00); /*set lower column address*/
OLEDWriteCmd(0x10); /*set higher column address*/
OLEDWriteCmd(0x40); /*set display start line*/
OLEDWriteCmd(0xB0); /*set page address*/
OLEDWriteCmd(0x81); /*contract control*/
OLEDWriteCmd(0x66); /*128*/
OLEDWriteCmd(0xA1); /*set segment remap*/
OLEDWriteCmd(0xA6); /*normal / reverse*/
OLEDWriteCmd(0xA8); /*multiplex ratio*/
OLEDWriteCmd(0x3F); /*duty = 1/64*/
OLEDWriteCmd(0xC8); /*Com scan direction*/
OLEDWriteCmd(0xD3); /*set display offset*/
OLEDWriteCmd(0x00);
OLEDWriteCmd(0xD5); /*set osc division*/
OLEDWriteCmd(0x80);
OLEDWriteCmd(0xD9); /*set pre-charge period*/
OLEDWriteCmd(0x1f);
OLEDWriteCmd(0xDA); /*set COM pins*/
OLEDWriteCmd(0x12);
OLEDWriteCmd(0xdb); /*set vcomh*/
OLEDWriteCmd(0x30);
OLEDWriteCmd(0x8d); /*set charge pump enable*/
OLEDWriteCmd(0x14);
OLEDPAGEADDRMODE();
OLEDCLEAR();
OLEDWriteCmd(0xAF); /*display ON*/
}
在OLED初始中实现对地址采用Page address mode
static void OLEDPAGEADDRMODE(void)
{
OLEDWriteCmd(0x20);
OLEDWriteCmd(0x02);
}
清屏幕
static void OLEDCLEAR(void)
{
int page,col,i;
for(page = 0; page <8;page++)
{
OLEDSETPOS(page,0);
for(i=0;i<128;i++)
{
OLEDWriteDAT(0);
}
}
}
地址/数据模式
OLED_DC:高电平表示数据,低电平表示数据。
/*命令*/
static void OLEDWriteCmd(unsigned int cmd)
{
OLED_SET_DC(0); /*COMMAND*/
OLED_SET_CS(0); /*选中OLED*/
SPISENDBYTE(cmd);
OLED_SET_CS(1); /*取消选中OLED*/
OLED_SET_DC(1); /*COMMAND*/
}
/*地址*/
static void OLEDWriteDAT(unsigned int dat)
{
OLED_SET_DC(1); /*data*/
OLED_SET_CS(0); /*选中OLED*/
SPISENDBYTE(dat);
OLED_SET_CS(1); /*取消选中OLED*/
OLED_SET_DC(1); /*data*/
}
根据传入0/1设置相应的GPIO口输出高低电平
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);
}
}
数据传输
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;
}
}
时钟设置
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);
}
}
程序到这里已经实现了CPU的数据传出,接下来需要将数据收到,以及打印到OLED的相应位置
void oledprint(int page,int col,char *str)
{
int i = 0;
while(str[i])
{
OLEDPUTCHAR(page,col,str[i]);
col += 8;
if(col > 127)
{
page += 2;
col = 0;
}
i++;
}
}
一个字节占8列,16行(16*8)
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]); /*原因是字符数组二维数组是16个为一组,前8个是上一排的,后8个是下一排*/
}
}
static void OLEDSETPOS(int page, int col)
{
OLEDWriteCmd(0xb0 + page); /*page address*/
OLEDWriteCmd(col & 0xf); /*Set Lower Column Start Address for Page Addressing Mode*/
OLEDWriteCmd(0x10 +(col>>4)); /*Set Higher Column Start Address for Page Addressing Mode*/
}
通过SPI协议实现对FLASH的操作
读厂家ID、设备ID
首先发送0x90,然后发出3个字节的0(是一个24位的数据,所以需要分割为3个字节来发送),然后GPG5就有数据读入。
void SPIflashreadID(int *pMID,int *pDID)
{
SPI_FLASH_set_cs(0); /*选中SPIflash*/
SPISENDBYTE(0X90);
SPIFlashSendAddr(0); /*当发完这些指令和地址是,GPGDAT5上就有数据传入*/
*pMID = SPIRecvByte();
*pDID = SPIRecvByte();
SPI_FLASH_set_cs(1); /*取消选中SPIflash*/
}
static void SPIFlashSendAddr(unsigned int addr)
{
SPISENDBYTE(addr>>16); /*地址由24位组成,先发高高8位*/
SPISENDBYTE((addr>>8)&0xff); /*地址由24位组成,再发高8位*/
SPISENDBYTE(addr&0xff); /*地址由24位组成,最后发低8位*/
}
static void SPI_FLASH_set_cs(char val)
{
if(val)
{
GPGDAT |= (1<<2); /*不选中*/
}
else
{
GPGDAT &= ~(1<<2); /*选中*/
}
}
/*主机传入*/
static char SPI_SET_DI(void)
{
/*需要判定GPGDAT第5位是什么数据*/
if(GPGDAT &(1<<5))
{
return 1;
}
else
{
return 0;
}
}
unsigned char SPIRecvByte(void)
{
int i;
unsigned char val = 0; /*用于存放本字节的东西*/
for(i=0;i<8;i++)
{
val <<= 1;
SPI_SET_CLK(0);
if(SPI_SET_DI())
{
val |= 1; /*数据为1就或1,否则就是0,前面左移已经产生了,所以不用赋值*/
}
SPI_SET_CLK(1);
}
return val;
}
对FLASH进行读写
对FLASH的写操作,需先进进行写使能,取出状态寄存器的保护,然后擦除,然后写入
写使能:
写保护:
static void SPIFlashWriteEnable(int enable)
{
if (enable)
{
SPI_FLASH_set_cs(0);
SPISENDBYTE(0x06);
SPI_FLASH_set_cs(1);
}
else
{
SPI_FLASH_set_cs(0);
SPISENDBYTE(0x04);
SPI_FLASH_set_cs(1);
}
}
读状态寄存器
发送0x05命令,接下来就可以读数据了
static unsigned char SPIFlashReadStatusReg1(void)
{
unsigned char val;
SPI_FLASH_set_cs(0);
SPISENDBYTE(0x05);
val = SPIRecvByte();
SPI_FLASH_set_cs(1);
return val;
}
static unsigned char SPIFlashReadStatusReg2(void)
{
unsigned char val;
SPI_FLASH_set_cs(0);
SPISENDBYTE(0x35);
val = SPIRecvByte();
SPI_FLASH_set_cs(1);
return val;
}
写状态寄存器
static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)
{
上一篇:用openocd+jtag并口小板调试qq2440
下一篇:十四、S3C2440 裸机 — MMU
推荐阅读最新更新时间:2024-11-09 11:16
推荐帖子
- MSP MCU I2C入门指南
- I2C(或称为I2C,集成电路总线)是一种两线制通信形式,主要用来在短距离、电路板间的应用中,实现微控制器与外设IC之间的低速通信。由于其采用范围很广,所以学习使用I2C与MSPMCU之间的通信已经在帮助工程师开发应用方面变得十分必要。通过使用一个超低功耗MSPMCU来访问和控制IC器件,这份指南提供了理解I2C协议并执行这一协议所需的工具与资源。器件术语驱动SCL时钟线路的器件被称为主器件,而对其进行响应的器件被称为从器件。在大多数应用中,MSPMCU为主器
- Jacktang DSP 与 ARM 处理器
- SWD无法下载,请问什么原因啊??
- JLinkinfo:------------DLL:V6.30h,compiledMar16201818:02:51Firmware:J-LinkV9compiledMar2201817:07:57Hardware:V9.40S/N:59425868Feature(s):RDI,GDB,FlashDL,FlashBP,JFlash,RDDI*JLinkInfo:STM32(connect):Cannotattach
- wdliming stm32/stm8
- 如何将U盘的物理(扇区)地址对应上盘符(windows OS)
- 我最近在做U盘分区,通过修改U盘主引导记录的分区表(DPT),可以将U盘分为1个以上的分区。修改DPT、格式化之后,系统只识别第一个分区。尽管我通过winhex将后面分区的DBR修改到和可识别分区的DBR一致,在windows系统的“计算机管理/磁盘管理”中也可以看到U盘中的新分区,并且显示状态良好,但就是无法格式化,也无法分配盘符。请问如何给U盘的新分区分配一个盘符?或者是将新增加的盘符映射到新分区中(通过指定U盘扇区的方式)?急!在线等!谢谢!如何将U盘的物理(扇区)地址对应上盘符
- 蒙蒙静 嵌入式系统
- 求msp430f2274使用内部AD读取正弦波的例程
- 求msp430f2274使用内部AD读取正弦波的例程,最好能有读取波形然后存储在MEM中再原样输出的例程,谢谢,自己弄了一个星期了没出来求msp430f2274使用内部AD读取正弦波的例程2274是10位ADC回复楼主fanfan1308的帖子楼主可以贴一些自己尝试过的程序。遇到哪些问题没解决的也标注下。方便别人帮忙解决有没有呀,求教导,求探讨,求分享回复板凳孤独剑的帖子#includemsp430x22x4.hunsignedintad_value;
- fanfan1308 微控制器 MCU
- 来看看老外搞的廉价冲锂电线路!
- 老外也喜欢便宜货啊!500){this.resized=true;this.width=500;this.alt=\'这是一张缩略图,点击可放大。\\n按住CTRL,滚动鼠标滚轮可自由缩放\';this.style.cursor=\'hand\'}\">500){this.resized=true;this.width=500;this.alt=\'这是一张缩略图,点击可放大。\\n按住CTRL,滚动鼠标滚轮可自由缩放\';this.style.cursor=\'hand\'
- zbz0529 电源技术
- 肖特基二极管应用注意事项有哪些?
- 肖特基二极管由于其快速开关速度和低正向压降,广泛应用于电源管理、射频和通讯设备等领域。然而,在使用肖特基二极管时,工程师需要注意一些关键的应用事项,以确保器件在电路中可靠运行,并最大化其性能优势。以下是几项重要的应用注意事项:1.正向电压降和功耗管理肖特基二极管的低正向压降是其主要优点之一,这意味着它在导通状态下损耗较小,适用于高效电源管理应用。然而,即便是较低的正向电压降,在高电流应用中也可能导致显著的功率损耗。为此,需要注意以下几点:选择合适的肖特基二极管:根据电路中的电流大小选择合
- 辰达行电子 玄铁RISC-V活动专区
设计资源 培训 开发板 精华推荐
- STM32F10xxx GPIO 应用示例,使用 STM32F10xxx SWJ-DP 引脚作为标准 I/O
- 【征集令】远程开关
- 使用 NXP Semiconductors 的 MC13892VK 的参考设计
- LTC2946IMS 双向功率监视器的典型应用,在前向路径中具有能量和电荷监视器
- 【长沙理工大学】双向DC-DC常国庆
- LT1506IR-3.3 5V 至 3.3V 降压转换器的典型应用电路
- 自用锂电池充电器
- CL520 线性开关 LED 驱动器的典型应用
- 使用 Analog Devices 的 LT3470IDDB 的参考设计
- DC2874A-A,使用 LTC3310S 5V、40A 同步降压 Silent Switcher 2 的演示板