基于STM32的12864串行时序的实现

来源:互联网 发布:淘宝怎么提升信誉 编辑:程序博客网 时间:2024/05/17 07:55

     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--;    }    *(str++) = '.';       do    {        temp = (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组}




1 0