1. 程式人生 > >基於STM32的12864序列時序的實現

基於STM32的12864序列時序的實現

     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組

}