基於STM32的12864序列時序的實現
阿新 • • 發佈:2019-01-26
12864液晶並口驅動程式用的比較多,但是考慮到有的時候微控制器或者MCU的IO口有限時就可以使用序列驅動方法。以下是12864液晶序列時序圖,下面就根據這個圖來分析一下12864序列時序的實現,只有真正弄清楚了時序圖才能真正瞭解序列傳輸的原理。
從圖上可以看出序列傳輸時需要用到CS,SCLK,SID三根訊號線,但是由於CS是高電平有效,所以也可以把CS長接高電平,那樣就只需要兩根線就OK了,當然當使用12864序列模式時,PSB引腳必須接低電平,復位RST引腳可以懸空不接,因為12864內部有上電覆位電路。
由於資料是傳輸是以一個位元組8bits為單位,所以下面貼出傳輸一個位元組的函式實現
void Write_8bits(uint W_bits) { uint i,Temp_data; for(i=0; i<8; i++)//總共移動八次,就可以把8bits資料全部傳完 { Temp_data = W_bits; Temp_data <<= i;//把資料依次左移 if((Temp_data&0x80)==0) //判斷對應位是否為0 { CLRSID;// SID = 0; CLRSCLK;//SCLK = 0;//在時鐘的下降沿把資料傳輸出去 SETSCLK;// SCLK = 1;//置高電平,為下次傳輸做準備 } else //對應位為1 { SETSID;//SID = 1; CLRSCLK;//SCLK = 0; SETSCLK; //SCLK = 1; } } }
從時序圖上可以看出,要想完整的把一個位元組的資料傳出去,需要傳送三次才能實現,也就是需要傳送三個位元組,這三個位元組分別是:命令控制字,位元組的高四位+低四位0組成的位元組,位元組的低四位+高四位0組成的位元組。
命令控制字11111RWRS0
RW代表讀還是寫液晶,為0代表寫,為1代表讀。RS代表寫命令還是資料,為0代表寫命令,為1代表寫資料,其餘6位固定。所以假設要向液晶寫資料,就必須首先發送11111010,寫命令就傳送11111000。
下面是程式的具體實現,這是關鍵!
void W_1byte(uchar RW, uchar RS, uchar W_data)
{
uint H_data,L_data,S_ID = 0xf8; //11111RWRS0
if(RW == 0)
{
S_ID &=~ 0x04;
}
else //if(RW==1)
{
S_ID |= 0X04;
}
if(RS == 0)
{
S_ID &=~ 0x02;
}
else //if(RS==1)
{
S_ID |= 0X02;
}
//以上是根據讀寫命令以及是傳送資料還是命令來組合命令字
H_data = W_data;
H_data &= 0xf0; //遮蔽低4位的資料
L_data = W_data; //xxxx0000格式
L_data &= 0x0f; //遮蔽高4位的資料
L_data <<= 4; //xxxx0000格式
SETCS;//CS = 1; 時能傳送
Write_8bits(S_ID); //傳送命令字S_ID
Write_8bits(H_data); //傳送H_data
Write_8bits(L_data); //傳送L_data
CLRCS;//CS=0
}
//寫入字串
void LCD_write_string(uchar *s)
{
for(uint i=0;s[i]!='\0';i++)
W_1byte(0,1,s[i]);
}
//液晶初始化函式
void LCD_Init_12864(void)//液晶初始化程式
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
CLRPSB;//PSB=0 序列模式
OSTimeDly (40);//關於這個延時根據使用的微控制器來決定我這裡使用STM32 所以要加延時
W_1byte(0,0,0x30);//功能設定 8位資料,基本指令
OSTimeDly (40);
W_1byte(0,0,0x02); //地址歸位
OSTimeDly (40);
W_1byte(0,0,0x06); //遊標及顯示右移一位
OSTimeDly (40);
W_1byte(0,0,0x0c); //顯示狀態 ON,遊標OFF,反白OFF
OSTimeDly (40);
W_1byte(0,0,0x01); //清除顯示
OSTimeDly (40);
W_1byte(0,0,0x81);//設定寫入地址
LCD_write_string("STM32 ADC");
}
//附上一個實用的程式:把數字轉換成字串,n是轉換的精度,即是字串'.'後有幾位小數
//有的時候要把AD取樣得到的浮點型轉換成字串在液晶上顯示,這個函式就非常有用
int ftoa(char *str, float num, int n)
{
int sumI;
float sumF;
int temp;
int count = 0;
char *p;
char *pp;
if(str == NULL) return -1;
p = str;
if(num < 0)
{
num = 0 - num;
}
sumI = (int)num; //sumI is the part of int
sumF = num - sumI; //sumF is the part of float
do
{
temp = sumI % 10;
*(str++) = temp+48;
}while((sumI = sumI /10) != 0);
pp = str;
pp--;
while(p < pp)
{
*p = *p + *pp;
*pp = *p - *pp;
*p = *p -*pp;
p++;
pp--;
}
*(st
r++) = '.';
do
{
tem
p = (int)(sumF*10);
*(str++) = temp+48;
if((++count) == n)
break;
sumF = sumF*10 - temp;
}while(!(sumF > -0.000001 && sumF < 0.000001));
*str='\0';
return 0;
}
//12864.h 12864標頭檔案
#include
#define uchar CPU_INT08U
#define uint CPU_INT16U
#define SETCS GPIO_SetBits(GPIOC,GPIO_Pin_9) //CS PC.9
#define SETSID GPIO_SetBits(GPIOC,GPIO_Pin_10) //SID PC.10
#define SETSCLK GPIO_SetBits(GPIOC,GPIO_Pin_11) //SCLK PC.11
#define SETPSB GPIO_SetBits(GPIOC,GPIO_Pin_12) //PSB PC.12
#define CLRCS GPIO_ResetBits(GPIOC,GPIO_Pin_9)
#define CLRSID GPIO_ResetBits(GPIOC,GPIO_Pin_10)
#define CLRSCLK GPIO_ResetBits(GPIOC,GPIO_Pin_11)
#define CLRPSB GPIO_ResetBits(GPIOC,GPIO_Pin_12)
void W_1byte(uchar RW, uchar RS, uchar W_data);
void LCD_Init_12864(void);
int ftoa(char *str, float num, int n);
void LCD_write_string(u8 *s);
//主函式 實現功能STM32 ADC取樣 在液晶上顯示
#include "12864.h"
#define ADC1_DR_Address ((uint32_t)0x4001244C) //ADC資料暫存器的基地址
USART_InitTypeDef USART_InitStructure; //串列埠、ADC、DMA宣告
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure; // 注:ADC為12位模數轉換器,只有ADCConvertedValue的低12位有效
uint16_t ADCConvertedValue;
void ADC_GPIO_Configuration(void);
static void Delay_ARMJISHU(__IO uint32_t nCount)
{
for (; nCount != 0; nCount--);
}
uchar tab1[]="0123456789";
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
u16 ADCConvertedValueLocal, Precent = 0, Voltage = 0;
ADC_GPIO_Configuration();
//Delay_ARMJISHU(1000000);//延時 上電覆位 不然需要手動復位
LCD_Init_12864();//液晶初始化程式
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA時鐘
DMA_DeInit(DMA1_Channel1); //開啟DMA1的第一通道
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //DMA對應的外設基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue; //記憶體儲存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //DMA的轉換模式為SRC模式,由外設搬移到記憶體
DMA_InitStructure.DMA_BufferSize = 1;//DMA快取大小,1個
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //接收一次資料後,裝置地址禁止後移
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //關閉接收一次資料後,目標記憶體地址後移
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //定義外設資料寬度為16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //DMA搬移資料尺寸,HalfWord就是為16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //轉換模式,迴圈快取模式。
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA優先順序高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //M2M模式禁用
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_GPIO_Configuration();
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //獨立的轉換模式 ADC_DUALMOD[3:0]=0000;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;//開啟掃描模式 ADC_SCAN=1;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//開啟連續轉換模式 ADC_CONT=1;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//ADC外部開關,關閉狀態 ; 軟體轉換 ADC_EXTSEL[2:0]=111;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//對齊方式,ADC為12位中,右對齊方式 ADC_ALIGN=0;
ADC_InitStructure.ADC_NbrOfChannel = 1;//開啟通道數,1個 ADC_SQR1[23:20]=0000;
//ADC_SQR1[23:20] 設定通道數目的選擇
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime_55Cycles5);
//ADC_SMPR2 ADC_SMPR1 設定每個通道的取樣時間
//ADC_SQR1[19:0]DC_SQR1[29:0]DC_SQR3[29:0] 設定對應通道的轉換順序 適用於多通道取樣
//ADC通道組, 第9個通道 取樣順序1,轉換時間
ADC_DMACmd(ADC1, ENABLE); //ADC命令,使能 ADC_ADON=1
ADC_Cmd(ADC1, ENABLE); //開啟ADC1 ADC_DMA=1
ADC_ResetCalibration(ADC1); //重新校準
while(ADC_GetResetCalibrationStatus(ADC1)); //等待重新校準完成
ADC_StartCalibration(ADC1); //開始校準 ADC_RSTCAL=1; 初始化校準暫存器
while(ADC_GetCalibrationStatus(ADC1)); //等待校準完成 ADC_CAL=0;
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //連續轉換開始,ADC通過DMA方式不斷的更新RAM區。
//ADC_SWSTART=1 開始規則轉換 切記 軟體觸發也屬於外部事件 要設定 ADC_EXTTRIG=1
while (1)
{
ADCConvertedValueLocal = ADCConvertedValue;
Precent = (ADCConvertedValueLocal*100/0x1000);//算出百分比
Voltage = Precent*33; //3.3V的電平,計算等效電平
W_1byte(0,0,0x91);
W_1byte(0,1,tab1[Precent/10]);
W_1byte(0,1,tab1[Precent]);
W_1byte(0,1,'%');
W_1byte(0,1,' ');
W_1byte(0,1,tab1[Voltage/1000]);
W_1byte(0,1,'.');
W_1byte(0,1,tab1[(Voltage00)/100]);
W_1byte(0,1,tab1[(Voltage0)/10]);
W_1byte(0,1,'V');
}
}
void ADC_GPIO_Configuration(void) //ADC配置函式
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA, ENABLE); //使能ADC和GPIOC時鐘
//PB1 作為模擬通道輸入引腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //管腳1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //輸入模式
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIO組
}