? 低功耗藍牙(BluetoothLow Energy),簡稱BLE。藍牙低能耗無線技術利用許多智能手段最大限度地降低功耗。
藍牙低能耗架構共有兩種芯片構成:單模芯片和雙模芯片。藍牙單模器件是藍牙規范中新出現的一種只支持藍牙低能耗技術的芯片——是專門針對ULP操作優化的技術的一部分。藍牙單模芯片可以和其它單模芯片及雙模芯片通信,此時后者需要使用自身架構中的藍牙低能耗技術部分進行收發數據。雙模芯片也能與標準藍牙技術及使用傳統藍牙架構的其它雙模芯片通信。
TI用于感測應用的藍牙低功耗裝置是真正的 SoC 解決方案。CC254x SoC 系列完美結合 TI 協議堆棧、基本軟件 (profile software) 以及樣品應用 (sample application),是高彈性、低成本單模藍牙低功耗解決方案。接下來我們將結合CC254x,講解從環境的搭建到藍牙4.0協議棧的開發來深入學習藍牙4.0的開發過程。本教程共分為六部分,主要知識點如下所示:
第一部分知識點:
第一節 BLE開發環境的搭建
第二節 BLE快速體驗
第三節 創建IAR工程-點亮LED
第四節 控制LED
第五節 LCD12864顯示
第二部分知識點:
第六節 獨立按鍵之查詢方式
第七節 獨立按鍵之中斷方式
第八節 CC254x內部溫度傳感器溫度采集
第九節 五向按鍵
第十節 蜂鳴器
第三部分知識點:
第十一節 串口通信
第十二節 Flash的讀寫
第十三節 BLE協議棧簡介
第十四節 OSAL工作原理
第十五節 BLE藍牙4.0協議棧啟動分析
第四部分知識點:
第十六節 協議棧LED實驗
第十七節 協議棧LCD顯示
第十八節 協議棧UART實驗
第十九節 協議棧五向按鍵
第二十節 協議棧Flash數據存儲
第五部分知識點:
第二十一節 DHT11溫濕度傳感器
第二十二節 藍牙協議棧之從機通訊
第二十三節 藍牙協議棧主從一體之主機通訊
第二十四節 OAD空中升級
第二十五節 SBL串口升級
第六部分知識點:
第二十六節 UBL-USB升級
第二十七節 MT-iBeacon基站使用iPhone空中升級
第二十八節 MT-iBeacon基站在PC端實現OAD空中升級
第二十九節 MT-iBeacon基站關于LightBlue軟件的使用
第三十節 如何使用MT-USBDongle的透傳功能
?
有關TI 的CC254x芯片介紹,可點擊下面鏈接查看:
?
BLE是藍牙4.0規范中的一種,其中master最多有7個外設,低功耗,低延遲,低吞吐量。
六種設備狀態
待機狀態(standby):設備沒有傳輸和發送數據,并且沒有連接到任何設備
廣播狀態(Advertiser):周期性廣播狀態
掃描狀態(Scanner):主動尋找正在廣播的設備
發起鏈接狀態(Initiator):主動向掃描設備發起連接。
主設備(Master):作為主設備連接到其他設備。
從設備(Slave):作為從設備連接到其他設備。
五種工作狀態
準備(standby),廣播(advertising),監聽掃描(Scanning),發起連接(Initiating),已連接(Connected)
四種設備類型
Cnetral主機(常作為client端):如手機,PC
Peripheral從機(常作為Service端):如心率計,血糖計
Observer觀察者:
Broadcast廣播者:
連接過程:
Peripheral開啟廣播-->Central掃描從機廣播-->Peripheral接收到Central的掃描請求,Peripheral向Central發送掃描回應數據-->Central向Peripheral發起連接-->開始通信。
兼容性
第一節 BLE開發環境的搭建
1.1 硬件準備
要進行BLE的開發,首先我們需要一個硬件環境。
?。?) MT254xBoard開發板(最好有兩塊,方便進行數據收發實驗);
(2) USBDongle-BLE抓包工具(多個固件,一個硬件多種用途),協議開發時輔助我們分析數據包;
?。?) 開發必備CC-Debug,用于下載和調試程序;
有關本文的工具下載,大家可以到以下這個地址:
朱兆祺ForARM1.2 BLE協議棧的安裝
我們使用的是最新版本的協議棧BLE-CC254x-1.4.0,首先在配套的資料文件夾中的tools文件夾下找到BLE-CC254x-1.4.0.exe文件。
我們提供了一個安裝包和一個免安裝的源碼,根據我的開發經驗,建議使用安裝包安裝到C盤,直接使用免安裝源碼在后期的開發中會遇到一些莫名其妙的問題。下面開始安裝協議棧,安裝方式很簡單,記得選擇C盤安裝。在安裝的最后階段,默認的會安裝Btool。
成功安裝了協議棧后,將會出現說明文件。在說明文件中我們可以看到,這個版本的協議棧需要使用IAR for 8051 8.20.2版本的軟件。
注:如果使用的是Win8以上的系統建議使用IAR for 8051 8.30.2版本的軟件,安裝方式和8.20.2是一樣的。
下面我們就開始安裝這個版本的軟件。
1.3 IAR安裝
在配套的文件目錄下找到如下文件。
安裝IAR,然后安裝Dongle驅動。
到這步,說明IAR已經安裝完成,下面開始進行和諧,你懂得!
解壓此文件夾,得到如下文件:
將解壓得到的文件全部復制到IAR安裝目錄(如下圖),直接覆蓋原始文件。
這樣IAR的安裝就完成了。先開啟軟件來體驗一下安裝成果吧!
1.4 安裝燒寫軟件
至此,我們目前需要用到的開發軟件就安裝完成了。
第二節 BLE快速體驗
經過前面的安裝,我們的開發環境已經搭建好了,現在我們先來體驗一下BLE,給自己點動力,comeon!使用SmartRFFlash Programmer燒寫從機固件:CC2540_SmartRF_SimpleBLEPeripheral.hex,燒寫方法見SmartRF Flash Programmer的使用章節。
協議棧默認自帶了一些已經編譯好的文件,可以直接燒寫,具體路徑如下圖:
從機固件路徑:
讀取設備的IEEE地址:
?
燒寫完成后,如果你有支持Ble的手機或平板就可以搜索到設備了,或者使用本公司開發的USBDongle(抓包固件或HostTestRelease固件)也可以搜索到設備,具體的使用可以閱讀相應的產品使用手冊,我這里用andriod平板搜索:
?
通過MAC地址可以知道我們的設備已經在正常的廣播了,我這里使用本公司開發的andriod端軟件TruthBlue可以正常搜索到我們的設備。如果用戶手上有支持BLE的設備并且系統在andriod4.3以上也可以安裝我們的這個軟件。
連接上設備后如圖,這里我們不要求大家能夠看懂這些,這里僅僅是為了體驗,后面的章節中我們會詳細的講述這些知識。
第三節 創建IAR工程-點亮LED
經過前面的準備工作,這章開始我們開始正式的開發過程。
這個教程是為有一定51基礎和C基礎的人準備的,如果讀者這方面還欠缺,請找相關方面的書籍惡補一下。CC2540的本質就是一個8051的單片機,所以我們裸機開發就可以作為一個51單片機來開發,裸機開發的目的是為了讓大家熟悉整個硬件以及開發環境,這并不是我們的最終目的,但這是一個必須的過程,為后面開發協議棧奠定基礎。
打開我們前面安裝的IAR軟件,創建一個新的工程。
因為我們使用的CC2540是增強型51單片機,這里我們創建一個空的8051工程,具體配置選項如圖:
選擇目錄保存工程;
我們這里創建一個最簡單的例程,點亮一個LED,這個例程就像我們學習每種編程語言是都是先來個Hello World!。雖然簡單,但是能夠讓我們最快的掌握一個開發環境的使用。
新的工程為空工程,沒有任何文件,我們這里新建一個文件并且保存為C文件。
添加文件到工程:
保存WorkSpace,在IAR中每個工程都必須要有一個Workspace,而且一個Workspace中可以有多個工程,所以這里我也必須要保存一個Workspace,點擊file->save Workspace As就會彈出如下對話框,這里和保存文件一樣需要對這個WorkSpace命名,我們這里一樣取名LED。
接下來我們需要對工程進行一些配置,使它適應我們的CPU。在工程處右擊,進入配置界面。
CPU配置:
這里我們第一個要做的就是選擇我們的CPU,我們使用的是TI公司生產的CC2540F256,所以這里選擇CC2540F256。配置好CPU后,我們還需要配置編譯輸出的文件格式,選擇到Linker選項,配置如下圖:
debug選項:
選項配置:
經過這些配置后,我們可以開始編碼了,下面開始編寫我們的第一個代碼,功能是點亮2個LED,開發板上有兩個LED燈,分別對應P1.0和P1.1。
代碼如下,可能覺得都是注釋,這里我還是建議大家有一個好的編碼風格,在開發大項目時就能夠看到它的優勢。
int main(void)
{
P1SEL &= ~0X03; // 將P1.1、0設置為IO功能
P1DIR |= 0X03; // 設置P1.1、0為輸出功能
while(1)
{
P1 = (P1 & 0XFC) | 0X01; // 設置P1.0輸出高電平
}
return 0;
}
編寫好代碼后,就可以編譯下載到開發板上了。點擊圖中所示圖標全速運行。
根據原理圖,P1.0對應的是LED2,這里我們能夠看到LED2處于點亮的狀態。
根據CC254X的數據手冊,我們可以很快知道P1SEL是設置IO功能,P1DIR是設置輸入輸出。至于為什么程序是這么寫,我們來看下,CC254X芯片的P1口一共有8個IO口,那就是說剛剛好由兩位十六進制進行控制:1111 1111(FF),這里僅僅是LED1和LED2,也就是P1.1和P1.0兩個IO口,為了不影響其他引腳的使用,我們這里巧妙使用與或控制其功能。比如:P1 = (P1 & 0XFC) | 0X01; P1與上1111 1100,這樣不影響其他引腳的基礎上,清除了P1.0和P1.1的輸出,再或上0X01,這樣將P1.0設置為高電平,根據原理圖,高電平是點亮LED2.
第四節 控制LED
上一節點亮了單個LED燈,我們這堂課接著控制LED燈。這堂課我們要完成的是LED閃爍10次,蜂鳴器響1s鐘。這里我們先使用延時函數進行。
我們的程序一定要做到結構清晰,可移植性強,閱讀性高。程序設計不僅僅是實現了功能,如果那樣的代碼,那只有你自己可以看懂,是一手垃圾。真正的漂亮代碼具有閱讀性高、可移植性強、代碼規范性好。
delay.h:
#ifndef __DELAY_H__
#define __DELAY_H__
extern void Delay1ms(unsigned int uiDelay);
#endif
/* end file */
延時函數的執行程序delay.c:
#include “delay.h”
void Delay1ms(unsigned int uiDelay)
{
unsigned int i;
for ( ; uiDelay > 0; uiDelay--)
{
/* 大約延時1ms */
for (i = 0; i <320; i++);
}
}
/* end file */
主函數其實也很簡單:
int main(void)
{
/* 控制LED燈閃爍 */
unsigned char i;
/* 驅動無源蜂鳴器 */
unsigned int j;
/* 將P1.0、P1.1設置為IO口 */
P1SEL &= ~0x03;
/* 將P1.0、P1.1設置為IO口的輸出 */
P1DIR |= 0x03;
/* 將P2.0設置為IO口 */
P2SEL &= ~0x01;
/* 將P2.0設置為IO口輸出 */
P2DIR |= 0x01;
/* 主循環 */
while(1)
{
/* LED1,LED2閃爍10次 */
for (i = 0; i <10; i++)
{
/* P1.0----LED2,P1.1----LED1 */
/* P1.0,P1.1輸出高電平,即點亮LED2,LED1 */
/* FC : 1111 1100*/
P1 = (P1 & 0xFC) | 0x03;
Delay1ms(1000);
/* P1.0,P1.1輸出低電平,即熄滅LED2,LED1 */
/* FC : 1111 1100*/
P1 = (P1 & 0xFC) & (~0x03);
Delay1ms(1000);
}
/* 給出500HZ的方波驅動 */
for(j = 0; j <1000; j++)
{
/* P2.0----蜂鳴器 */
P2 = (P2 & 0xFE) & (~0x01);
Delay1ms(1);
P2 = (P2 & 0xFE) | 0x01;
Delay1ms(1);
}
}
}
/* end file */
這里需要注意的是,MT254X藍牙4.0開發板使用的無源蜂鳴器,那么我們需要產生一個方波來驅動。如這代碼:
/* 給出500HZ的方波驅動 */
for(j = 0; j <1000; j++)
{
/* P2.0----蜂鳴器 */
P2 = (P2 & 0xFE) & (~0x01);
Delay1ms(1);
P2 = (P2 & 0xFE) | 0x01;
Delay1ms(1);
}
如果是有緣蜂鳴器,則沒有那么麻煩,直接給出低電平驅動。為什么是低電平,我們看下原理圖:
使用的PNP三極管,并且使用續流二極管保護蜂鳴器。
第五節 LCD12864顯示
上一節我們成功控制了LED和蜂鳴器,這一節我們馬不停蹄接著LCD12864的控制。
為了系統能夠穩定的工作,首先我們將系統時鐘切換到32M的外部晶振,為了自由配置所需要的時鐘,主要借助于CLKCONCMD.OSC選擇系統主時鐘,而借助于CLKCONCMD.OSC32K則用于選擇芯片32K時鐘源!而低功耗模式設置時,需要借助于SLEEPCMD寄存器,在《CC253x- CC2540-41Applications User‘s Guide.pdf>中并沒有說明SLEEPCMD第二位功能,如下所示:
但是參考cc2430芯片的說明書可以發現,對應的SLEEP寄存器則有說明,如下所示,這個是TI有意隱藏芯片細節,當SLEEPCMD.OSC_PD為0時,32MHz晶振與16MHz RC振蕩器都會起振:
對于SLEEPSTA寄存器中BIT6/BIT5說明在cc2530說明書中也并沒有說明,可以參考cc2430說明書中內容,其中第6位 XOSC_STB表明外部高速32M晶振是否上電并穩定起振,當穩定時該位為1;同樣對于第5位HFRC_STB則表明內部16MHz高速RC振蕩器是否起振,并是否穩定,當16MHz RC振蕩器穩定時該位為1。
void SysStartXOSC(void)
{
SLEEPCMD &= ~0x04; // 啟動所有晶振
while (!(SLEEPSTA & 0x40)); // 等待晶振穩定
CLKCONCMD = (CLKCONCMD & 0x80) | 0x49; // 使用16M晶振作為主時鐘
while ((CLKCONSTA & ~0x80) != 0x49 ); // 等待主時鐘切換到16M晶振
CLKCONCMD = (CLKCONCMD & ~0x80) ; // 使用外部32K晶振作為休眠時鐘
while ( (CLKCONSTA & 0x80) != 0 ); // 等待睡眠時鐘切換到外部32K晶振
CLKCONCMD = (CLKCONCMD & 0x80) ; // 使用32M晶振作為主時鐘
while ( (CLKCONSTA & ~0x80) != 0 ); // 等待主時鐘切換到32M晶振
SLEEPCMD |= 0x04; // 關閉未使用的晶振
}
按照上述方式配置后,我們就可以工作在外部的32M晶振上了,配置好系統時鐘和SPI后,剩下的工作只需要按照液晶屏的說明書發送相應的指令就可以將液晶屏驅動起來了,具體的驅動代碼詳見下一堂課程。這里使用的是ASCII的點陣表,所以只能顯示英文,如果需要顯示中文,就需要中文字庫的支持了。
LCD12864的驅動程序:
P0.1 - LCD_MODE
P1.2 - LCD_CS
//spi
P1.5 - CLK
P1.6 - MOSI
/* LCD lines */
#define LCD12864_MAX_LINE 64
#define LCD12864_MAX_ROW 128
#define HAL_LCD_FONT_LINES 8
#define HAL_LCD_FONT_ROWS 6
/* LCD Max Chars and Buffer */
#define HAL_LCD_MAX_LINES (LCD12864_MAX_LINE/HAL_LCD_FONT_LINES) // 6*8點陣最大行數
#define HAL_LCD_MAX_CHARS (LCD12864_MAX_ROW/HAL_LCD_FONT_ROWS) // 6*8點陣最大列數
/* LCD Control lines */
#define HAL_LCD_RS_PORT 0
#define HAL_LCD_RS_PIN 1
#define HAL_LCD_CS_PORT 1
#define HAL_LCD_CS_PIN 2
/* LCD SPI lines */
#define HAL_LCD_CLK_PORT 1
#define HAL_LCD_CLK_PIN 5
#define HAL_LCD_MOSI_PORT 1
#define HAL_LCD_MOSI_PIN 6
// 12864 命令
#define LCD_CMD_DISPLAY_ON 0xAF
#define LCD_CMD_DISPLAY_OFF 0xAE
#define LCD_CMD_BEGIN_LINE 0x40
#define LCD_CMD_PAGE_LINE 0xB0
#define LCD_CMD_ROW_HIG 0x10
#define LCD_CMD_ROW_LOW 0x00
#define LCD_CMD_READ_STATE 0x00
#define LCD_CMD_ROW_ADDR_NORMAL 0xA0 // 從左到右
#define LCD_CMD_ROW_ADDR_REVERSE 0xA1 // 從右到左
#define LCD_CMD_DISPLAY_NORMAL 0xA6
#define LCD_CMD_DISPLAY_REVERSE 0xA7
#define LCD_CMD_DISPLAY_POINT_ALL 0xA5
#define LCD_CMD_DISPLAY_POINT_NORMAL 0xA4
#define LCD_CMD_BIAS_SET 0xA2 // 0XA2:BIAS=1/9 (常用) 0XA3:BIAS=1/7
#define LCD_CMD_SOFT_RESET 0xE2
#define LCD_CMD_LINE_NORMAL 0xC0 // 從上到下
#define LCD_CMD_LINE_REVERSE 0xC8 // 從下到上
#define LCD_CMD_POWER_ONE 0x2C
#define LCD_CMD_POWER_TWO 0x2E
#define LCD_CMD_POWER_THREE 0x2F
#define LCD_CMD_CONTRAST_ONE_LEVEL 0x22 // 0x20-0x27
#define LCD_CMD_CONTRAST_TWO_CMD 0x81 // 0x00-0x3F
#define LCD_CMD_STATIC_PICTURE_ON 0xAD
/* SPI interface control */
#define LCD_SPI_BEGIN() HAL_CONFIG_IO_OUTPUT(HAL_LCD_CS_PORT, HAL_LCD_CS_PIN, 0); /* chip select */
#define LCD_SPI_END()
{
asm(“NOP”);
asm(“NOP”);
asm(“NOP”);
asm(“NOP”);
HAL_CONFIG_IO_OUTPUT(HAL_LCD_CS_PORT, HAL_LCD_CS_PIN, 1); /* chip select */
}
/* clear the received and transmit byte status, write tx data to buffer, wait till transmit done */
#define LCD_SPI_TX(x) { U1CSR &= ~(BV(2) | BV(1)); U1DBUF = x; while( ?。║1CSR & BV(1)) ); }
/* Control macros */
#define LCD_DO_WRITE() HAL_CONFIG_IO_OUTPUT(HAL_LCD_RS_PORT, HAL_LCD_RS_PIN, 1);
#define LCD_DO_CONTROL() HAL_CONFIG_IO_OUTPUT(HAL_LCD_RS_PORT, HAL_LCD_RS_PIN, 0);
/*全體ASCII 列表:5x7點陣庫*/
const static uint8 aucAsciiTable5x7[][5]={
0x00,0x00,0x00,0x00,0x00,//space
0x00,0x00,0x4f,0x00,0x00,//!
0x00,0x07,0x00,0x07,0x00,//“
0x14,0x7f,0x14,0x7f,0x14,//#
0x24,0x2a,0x7f,0x2a,0x12,//$
0x23,0x13,0x08,0x64,0x62,//%
0x36,0x49,0x55,0x22,0x50,//&
0x00,0x05,0x07,0x00,0x00,//]
0x00,0x1c,0x22,0x41,0x00,//(
0x00,0x41,0x22,0x1c,0x00,//)
0x14,0x08,0x3e,0x08,0x14,//*
0x08,0x08,0x3e,0x08,0x08,//+
0x00,0x50,0x30,0x00,0x00,//,
0x08,0x08,0x08,0x08,0x08,//-
0x00,0x60,0x60,0x00,0x00,//。
0x20,0x10,0x08,0x04,0x02,///
0x3e,0x51,0x49,0x45,0x3e,//0
0x00,0x42,0x7f,0x40,0x00,//1
0x42,0x61,0x51,0x49,0x46,//2
0x21,0x41,0x45,0x4b,0x31,//3
0x18,0x14,0x12,0x7f,0x10,//4
0x27,0x45,0x45,0x45,0x39,//5
0x3c,0x4a,0x49,0x49,0x30,//6
0x01,0x71,0x09,0x05,0x03,//7
0x36,0x49,0x49,0x49,0x36,//8
0x06,0x49,0x49,0x29,0x1e,//9
0x00,0x36,0x36,0x00,0x00,//:
0x00,0x56,0x36,0x00,0x00,//;
0x08,0x14,0x22,0x41,0x00,//《
0x14,0x14,0x14,0x14,0x14,//=
0x00,0x41,0x22,0x14,0x08,//>
0x02,0x01,0x51,0x09,0x06,//?
0x32,0x49,0x79,0x41,0x3e,//@
0x7e,0x11,0x11,0x11,0x7e,//A
0x7f,0x49,0x49,0x49,0x36,//B
0x3e,0x41,0x41,0x41,0x22,//C
0x7f,0x41,0x41,0x22,0x1c,//D
0x7f,0x49,0x49,0x49,0x41,//E
0x7f,0x09,0x09,0x09,0x01,//F
0x3e,0x41,0x49,0x49,0x7a,//G
0x7f,0x08,0x08,0x08,0x7f,//H
0x00,0x41,0x7f,0x41,0x00,//I
0x20,0x40,0x41,0x3f,0x01,//J
0x7f,0x08,0x14,0x22,0x41,//K
0x7f,0x40,0x40,0x40,0x40,//L
0x7f,0x02,0x0c,0x02,0x7f,//M
0x7f,0x04,0x08,0x10,0x7f,//N
0x3e,0x41,0x41,0x41,0x3e,//O
0x7f,0x09,0x09,0x09,0x06,//P
0x3e,0x41,0x51,0x21,0x5e,//Q
0x7f,0x09,0x19,0x29,0x46,//R
0x46,0x49,0x49,0x49,0x31,//S
0x01,0x01,0x7f,0x01,0x01,//T
0x3f,0x40,0x40,0x40,0x3f,//U
0x1f,0x20,0x40,0x20,0x1f,//V
0x3f,0x40,0x38,0x40,0x3f,//W
0x63,0x14,0x08,0x14,0x63,//X
0x07,0x08,0x70,0x08,0x07,//Y
0x61,0x51,0x49,0x45,0x43,//Z
0x00,0x7f,0x41,0x41,0x00,//[
0x02,0x04,0x08,0x10,0x20,// 斜杠
0x00,0x41,0x41,0x7f,0x00,//]
0x04,0x02,0x01,0x02,0x04,//^
0x40,0x40,0x40,0x40,0x40,//_
0x01,0x02,0x04,0x00,0x00,//`
0x20,0x54,0x54,0x54,0x78,//a
0x7f,0x48,0x48,0x48,0x30,//b
0x38,0x44,0x44,0x44,0x44,//c
0x30,0x48,0x48,0x48,0x7f,//d
0x38,0x54,0x54,0x54,0x58,//e
0x00,0x08,0x7e,0x09,0x02,//f
0x48,0x54,0x54,0x54,0x3c,//g
0x7f,0x08,0x08,0x08,0x70,//h
0x00,0x00,0x7a,0x00,0x00,//i
0x20,0x40,0x40,0x3d,0x00,//j
0x7f,0x20,0x28,0x44,0x00,//k
0x00,0x41,0x7f,0x40,0x00,//l
0x7c,0x04,0x38,0x04,0x7c,//m
0x7c,0x08,0x04,0x04,0x78,//n
0x38,0x44,0x44,0x44,0x38,//o
0x7c,0x14,0x14,0x14,0x08,//p
0x08,0x14,0x14,0x14,0x7c,//q
0x7c,0x08,0x04,0x04,0x08,//r
0x48,0x54,0x54,0x54,0x24,//s
0x04,0x04,0x3f,0x44,0x24,//t
0x3c,0x40,0x40,0x40,0x3c,//u
0x1c,0x20,0x40,0x20,0x1c,//v
0x3c,0x40,0x30,0x40,0x3c,//w
0x44,0x28,0x10,0x28,0x44,//x
0x04,0x48,0x30,0x08,0x04,//y
0x44,0x64,0x54,0x4c,0x44,//z
0x08,0x36,0x41,0x41,0x00,//{
0x00,0x00,0x77,0x00,0x00,//|
0x00,0x41,0x41,0x36,0x08,//}
0x04,0x02,0x02,0x02,0x01,//~
};
const uint8 asciiTableSize = sizeof( aucAsciiTable5x7 ) / sizeof( aucAsciiTable5x7[0]);
static void LCD12864_Cmd(uint8 cmd)
{
LCD_SPI_BEGIN();
LCD_DO_CONTROL();
LCD_SPI_TX(cmd);
LCD_SPI_END();
}
static void LCD12864_Dat(uint8 data)
{
LCD_SPI_BEGIN();
LCD_DO_WRITE();
LCD_SPI_TX(data);
LCD_SPI_END();
}
void LCD12864_Init(void)
{
PERCFG |= 0x02; // 設置UART alt2 為 SPI
// 配置引腳為SPI功能
HAL_CONFIG_IO_PERIPHERAL(HAL_LCD_CLK_PORT, HAL_LCD_CLK_PIN);
HAL_CONFIG_IO_PERIPHERAL(HAL_LCD_MOSI_PORT, HAL_LCD_MOSI_PIN);
/* Configure SPI */
U1UCR = 0x80; // 清除原來的數據
U1CSR = 0x00; // SPI 主機模式
// 高位在前,第一個上升沿發送數據,波特率為2M
U1GCR = HAL_SPI_TRANSFER_MSB_FIRST | HAL_SPI_CLOCK_PHA_0 | HAL_SPI_CLOCK_POL_LO | 0x0F;
U1BAUD = 0xFF;
// CS RS 配置為輸出
HAL_CONFIG_IO_OUTPUT(HAL_LCD_RS_PORT, HAL_LCD_RS_PIN, 1);
HAL_CONFIG_IO_OUTPUT(HAL_LCD_CS_PORT, HAL_LCD_CS_PIN, 1);
SoftWaitUs(15000); // 15 ms
LCD12864_Cmd(LCD_CMD_SOFT_RESET); //軟復位
SoftWaitUs(15000); // 15 ms
LCD12864_Cmd(LCD_CMD_POWER_ONE); //升壓步聚1
SoftWaitUs(15); // 15 us
LCD12864_Cmd(LCD_CMD_POWER_TWO); //升壓步聚2
SoftWaitUs(15); // 15 us
LCD12864_Cmd(LCD_CMD_POWER_THREE); //升壓步聚3
SoftWaitUs(150); // 15 us
LCD12864_Cmd(LCD_CMD_CONTRAST_ONE_LEVEL); //粗調對比度,可設置范圍0x20~0x27
LCD12864_Cmd(LCD_CMD_CONTRAST_TWO_CMD); //微調對比度
LCD12864_Cmd(0x3a); //0x1a,微調對比度的值,可設置范圍0x00~0x3f
LCD12864_Cmd(LCD_CMD_BIAS_SET); // 1/9偏壓比(bias)
LCD12864_Cmd(LCD_CMD_LINE_NORMAL); //行掃描順序:從上到下
LCD12864_Cmd(LCD_CMD_ROW_ADDR_REVERSE); //列掃描順序:從左到右
LCD12864_Cmd(LCD_CMD_BEGIN_LINE); //起始行:第一行開始
LCD12864_Cmd(LCD_CMD_DISPLAY_ON); //打開顯示
LCD12864_Cmd(LCD_CMD_DISPLAY_POINT_NORMAL);
LCD12864_Cmd(LCD_CMD_DISPLAY_NORMAL); //設置為正顯模式
SoftWaitUs(150); // 150 us
}
static void LCD12864_SetAddr(uint8 line, uint8 col)
{
uint8 ucLine, ucRow;
//line += 5;
col += 4;
if((line >= LCD12864_MAX_LINE) || (col >= LCD12864_MAX_ROW))
{
return;
}
ucLine = LCD_CMD_PAGE_LINE | (line&0x0f);
LCD12864_Cmd(ucLine);
SoftWaitUs(15);
ucRow = LCD_CMD_ROW_HIG | (col>>4);
LCD12864_Cmd(ucRow);
SoftWaitUs(15); // 15 us
ucRow = LCD_CMD_ROW_LOW | (col&0x0f);
LCD12864_Cmd(ucRow);
SoftWaitUs(15); // 15 us
}
static void LCD12864_Dis5X8(char ch)
{
uint8 ucCnt;
if((ch >= 0x20)&&(ch <0x7f))
{
uint8 ucChar = ch - 0x20;
for(ucCnt=0; ucCnt《5; ucCnt++)
{
LCD12864_Dat( aucAsciiTable5x7[ucChar][ucCnt]);
}
//LCD12864_Dat(0x00);
}
else if(ch==0x00) //不需要顯示,清空指定位置
{
for(ucCnt=0; ucCnt《5; ucCnt++)
{
LCD12864_Dat(0x00);
}
}
LCD12864_Dat(0x00);
}
void LCD12864_Clear(void)
{
uint8 ucLine, ucRow;
for(ucLine=0; ucLine《LCD12864_MAX_LINE; ucLine++)
{
LCD12864_SetAddr(ucLine, 0);
for(ucRow=0; ucRow《LCD12864_MAX_ROW; ucRow++)
{
LCD12864_Dat(0x00);
}
}
}
void LCD12864_DisChar(uint8 line, uint8 col, char ch)
{
if (( line
{
LCD12864_SetAddr(line, col*HAL_LCD_FONT_ROWS);
LCD12864_Dis5X8(ch);
}
}
void LCD12864_DisStr(uint8 line, char* pStr)
{
uint8 ucCnt = 0;
for ( ucCnt = 0 ; ucCnt
{
if ( ’
主站蜘蛛池模板:
欧美日韩亚洲成人|
日本高清免费一本视频在线观看|
超碰免费视频在线观看|
成人免费在线观看|
国产亚洲精品久久久久久线投注
|
DASD-700美谷朱里|
国产高清免费观看|
美女gif趴跪式动态图|
四房播播最新地址|
2021精品国产综合久久|
国产午夜电影在线观看不卡|
爽爽影院免费观看|
国产AV国产精品国产三级在线L|
金发欧美一区在线观看|
日韩人妻精品久久日|
曰本aaaaa毛片午夜网站|
第一福利视频网站在线|
两性午夜刺激爽爽视频|
亚洲 欧美 国产 综合不卡|
99国产精品久久久久久久日本竹|
国产中文字幕乱码免费|
日韩欧美一级|
国产AV综合手机在线观看|
日日噜噜大屁股熟妇|
99久久99久久久精品久久|
久久re6热在线视频精品66|
无码欧美XXXXX在线观看裸|
99久久精品6在线播放|
女仆乖H调教跪趴|
最新国产精品福利2020|
寂寞少妇直播|
中文字幕免费在线视频|
国产伊人自拍|
午夜4k最新福利|
高清午夜福利电影在线|
天堂Av亚洲欧美日韩国产综合|
超碰在线vip|
性绞姿始动作动态图|
国产成人精品视频免费大全|
欧美视频 亚洲视频|
德国黄色录像|