生活随笔
收集整理的這篇文章主要介紹了
SPI协议介绍
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
目錄
SPI簡介
數(shù)據(jù)傳輸配圖
SPI模式
舉例OLED
軟件SPI
硬件SPI
SPI簡介
SPI協(xié)議是一種同步串行接口技術(shù)(Serial Peripheral Interface)的縮寫,是一種高速的,全雙工,同步的通信總線,SPI是一主多從的,SPI通信都是由主機發(fā)起的,SPI只需要4根線
- CS:片選引腳,多個SPI設(shè)備都會有一個片選引腳,當引腳拉低后就能選中該芯片
- SCK:串行時鐘,為SPI通信提供時鐘
- MISO/SDO:主設(shè)備數(shù)據(jù)輸入,從設(shè)備數(shù)據(jù)輸出
- MOSI/SDI:主設(shè)備數(shù)據(jù)輸出,從設(shè)備數(shù)據(jù)輸入
?
數(shù)據(jù)傳輸配圖
例如我們發(fā)出0x56的數(shù)據(jù)即0b0101 0110,首先我們需要選中引腳CS,然后數(shù)據(jù)從最高位0開始傳輸讓DO引腳變?yōu)?,假設(shè)SCK是在上升沿的時候數(shù)據(jù)發(fā)送出去,執(zhí)行8次,最后再取消片選引腳
?
SPI模式
SPI有四種工作模式,CPOL表示SPICLK初始狀態(tài)是高電平還是低電平,CPHA表示在第幾個時鐘沿采樣數(shù)據(jù),具體選擇哪種模式主要取決于外接的SPI設(shè)備
| ? | CPOL | CPHA |
| 模式0 | 0 | 0 |
| 模式1 | 0 | 1 |
| 模式2 | 1 | 0 |
| 模式3 | 1 | 11 |
CPOL為高電平一般SCK初始狀態(tài)為高電平,CPHA為高電平一般在第二個上升沿采樣數(shù)據(jù),經(jīng)常使用模式0和模式3,因為模式0和模式3都是在上升沿采樣數(shù)據(jù),不需要我們在乎SPICLK的初始狀態(tài)是什么,只要在上升沿采樣數(shù)據(jù)就行了
?
舉例OLED
對于OLED來說只需要單向傳輸,因此只需要CS、SCK和DO三條線,并不需要讀取OLED的,這里通過軟件來模擬SPI和硬件SPI兩種方式來實現(xiàn),首先需要實現(xiàn)SPI的寫操作例如gpio_spi.c,然后寫出oled的控制oled.c
軟件SPI
- 首先先將OLED的引腳初始化為輸出或者輸入狀態(tài)
- 對于OLED來說,其中OELD_DC用來控制發(fā)出的是命令還是數(shù)據(jù)
static void SPI_GPIO_Init(void)
{/* GPF1 OLED_CSn output */GPFCON &= ~(3<<(1*2));GPFCON |= (1<<(1*2));GPFDAT |= (1<<1);/* 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);
}
- OLED首先需要初始化,根據(jù)芯片手冊來查看初始化的過程
void OLEDInit(void)
{/* 向OLED發(fā)命令以初始化 */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); OLEDSetPageAddrMode();OLEDClear();OLEDWriteCmd(0xAF); /*display ON*/
}
- 寫出OLEDWriteCmd函數(shù),根據(jù)SPI協(xié)議我們首先需要選中引腳即將CS引腳拉低,對于OLED發(fā)送命令需要將DC引腳拉低,同理我們可以寫出發(fā)出數(shù)據(jù)的函數(shù)
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); /* */
}static void OLEDWriteDat(unsigned char dat)
{OLED_Set_DC(1); /* data */OLED_Set_CS(0); /* select OLED */SPISendByte(dat);OLED_Set_CS(1); /* de-select OLED */OLED_Set_DC(1); /* */
}
- 由于OLED的SCLK的初始引腳為低電平或者高電平都可以,同時在上升沿讀取數(shù)據(jù)
- 對于SPISendByte函數(shù)我們在gpio_spi.c中實現(xiàn),如下,參考數(shù)據(jù)傳輸配圖我們就可以寫出了OLED的命令和數(shù)據(jù)函數(shù)
static void SPI_Set_CLK(char val)
{if (val)GPGDAT |= (1<<7);elseGPGDAT &= ~(1<<7);
}static void SPI_Set_DO(char val)
{if (val)GPGDAT |= (1<<6);elseGPGDAT &= ~(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;}
}
- 雖然OLED就不需要DI,有些SPI設(shè)備例如Flash就需要我們通樣也可以寫出輸入函數(shù)
static char SPI_Get_DI(void)
{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_Get_DI())val |= 1;SPI_Set_CLK(1);}return val;
}
- 現(xiàn)在來實現(xiàn)OLED的打印函數(shù),對于個人的OLED來說有三種尋址方式,這里采用頁地址模式,OLED的分辨率128*64,對于這顯存128*64bit,顯存中對于字節(jié)每個對應(yīng)著像素點列向8位,注意不是橫向,頁地址模式8行表示1頁
- 現(xiàn)在要顯示8*16像素的字符,對于OLED來說,每寫一位地址自動加一,64行代表著8也,8*16即一個字符占2頁,寫出OLEDPrint函數(shù)如下,OLEDPutChar實現(xiàn)打印一個字符,對于第一個字符我們需要寫兩次,首先在第一頁發(fā)出8字節(jié)數(shù)據(jù),頁加一再發(fā)出8字節(jié)數(shù)據(jù),其中oled_asc2_8x16為8*16字庫,在網(wǎng)上找,OLEDSetPos設(shè)置顯存的地址,后面解釋
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 */
}/* 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 - ' '];/* 發(fā)給OLED */OLEDSetPos(page, col);/* 發(fā)出8字節(jié)數(shù)據(jù) */for (i = 0; i < 8; i++)OLEDWriteDat(dots[i]);OLEDSetPos(page+1, col);/* 發(fā)出8字節(jié)數(shù)據(jù) */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++;}
}
- 對于?OLEDSetPos函數(shù),在頁模式下頁地址從0xB0開始,行地址我們需要先發(fā)出低4位,再發(fā)出高四位,注意第五位D4為1
- 對于OLED清屏函數(shù),只需要再每個像素寫入0就行了
static void OLEDClear(void)
{int page, i;for (page = 0; page < 8; page ++){OLEDSetPos(page, 0);for (i = 0; i < 128; i++)OLEDWriteDat(0);}
}
- 設(shè)置頁地址模式,在OLED的命令表格中查找
- 需要發(fā)出2次,其中A1,A0為10即0x20就是頁地址模式,我們將其寫在OLED的初始化OLEDInit中,這樣我們初始化完OLED后就可以根據(jù)OLEDPrint函數(shù)來打印字符串了
static void OLEDSetPageAddrMode(void)
{OLEDWriteCmd(0x20);OLEDWriteCmd(0x02);
}
硬件SPI
硬件SPI沒什么好說的直接懟寄存器
#include "s3c24xx.h"/* SPI controller */static void SPI_GPIO_Init(void)
{/* GPF1 OLED_CSn output */GPFCON &= ~(3<<(1*2));GPFCON |= (1<<(1*2));GPFDAT |= (1<<1);/* GPG2 FLASH_CSn output* GPG4 OLED_DC output* GPG5 SPIMISO * GPG6 SPIMOSI * GPG7 SPICLK */GPGCON &= ~((3<<(2*2)) | (3<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2)));GPGCON |= ((1<<(2*2)) | (1<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2)));GPGDAT |= (1<<2);
}void SPISendByte(unsigned char val)
{while (!(SPSTA1 & 1));SPTDAT1 = val;
}unsigned char SPIRecvByte(void)
{SPTDAT1 = 0xff; while (!(SPSTA1 & 1));return SPRDAT1;
}static void SPIControllerInit(void)
{/* OLED : 100ns, 10MHz* FLASH : 104MHz* 取10MHz* 10 = 50 / 2 / (Prescaler value + 1)* Prescaler value = 1.5 = 2* Baud rate = 50/2/3=8.3MHz*/SPPRE0 = 2;SPPRE1 = 2;/* [6:5] : 00, polling mode* [4] : 1 = enable * [3] : 1 = master* [2] : 0* [1] : 0 = format A* [0] : 0 = normal mode*/SPCON0 = (1<<4) | (1<<3);SPCON1 = (1<<4) | (1<<3);}void SPIInit(void)
{/* 初始化引腳 */SPI_GPIO_Init();SPIControllerInit();
}
?
總結(jié)
以上是生活随笔為你收集整理的SPI协议介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。