Lcd1602 for STM32

Keil的编译优化问题:http://www.360doc.com/content/11/0126/14/2379862_89135875.shtml
/***********Lcd1602 for STM32 ********************/	
/*
 *BUG:打开测忙函数不能显示,由于STM32 比 LCD1602 相应速度较快,
 *ns级的单片机需要等待us级的LCD,(可以通过延时来实现)
 *否则会造成数据读写溢出
 */
/*
 * 1602 的 供电电压为5V,显示比较清晰,如跟STM32一样用3.3V供电则比较模糊
 */
#include "stm32f10x_lib.h"

/********************************硬件接口定义*********************************/
#define RS  GPIO_Pin_1 //P2^6-1; 4-3             
#define RW  GPIO_Pin_2 //P2^5-2; 5-2
#define EN  GPIO_Pin_0 //P2^7-0; 6-1

/********************************宏定义*********************************/
#define    Lcd_RS(x)  x ? GPIO_SetBits(GPIOB, RS): GPIO_ResetBits(GPIOB, RS)
#define    Lcd_RW(x)  x ? GPIO_SetBits(GPIOB, RW): GPIO_ResetBits(GPIOB, RW)
#define    Lcd_EN(x)  x ? GPIO_SetBits(GPIOB, EN): GPIO_ResetBits(GPIOB, EN)

/******************************** 变量定义 --------------------------------------*/
GPIO_InitTypeDef GPIO_InitStructure;     //GPIO
ErrorStatus HSEStartUpStatus;

	
typedef unsigned char BYTE;
typedef unsigned char BOOL; 

BYTE dis1[] = {"  WELCOME   TO  "};
BYTE dis2[] = {" STM32  Lcd1602 "};

/*********************************声明函数 ---------------------------------------*/
void RCC_Configuration(void);
void NVIC_Configuration(void);

/*****************************延时函数*********************************************/
void delay_nus(u32 n)//微秒级延时,通常情况此函数可以不用更改
{
    u8 i;
    while(n--)
    {
        i=7;
        while(i--);
  }
}

void delay_nms(u32 n) //毫秒级延时,通常情况此函数可以不用更改
{
    while(n--)
	{
        delay_nus(1100);
	}
}


/******************************测忙************************************************/	
BOOL Lcd_Busy()
{                         // 测试Lcd忙碌状态
	vu8 temp = 0x00ff;
	GPIO_Write(GPIOB, 0xffff);
	Lcd_RS(0);  
	delay_nus(10);
	Lcd_RW(1); 
	delay_nus(10); 
	Lcd_EN(1);
	delay_nus(10);

    while((GPIO_ReadInputData(GPIOB) & 0x8000)>>8)
	{
		delay_nus(20);
	}

	delay_nus(10);
	Lcd_EN(0);
	delay_nus(10);

	return (BOOL)0;
}
/******************************写命令*********************************************/	
Lcd_Wcmd(vu16 cmd)
{                          
	//while(Lcd_Busy());
	Lcd_EN(0);
	delay_nus(1);
	Lcd_RS(0); // 写入指令数据到Lcd
	delay_nus(1);
	Lcd_RW(0); 
    delay_nus(1);

	GPIOB->BSRR = cmd<<8 & 0xff00;  //将数据送到P0口 
	GPIOB->BRR = ((~cmd)<<8) & 0xff00;
    delay_nus(500);

	Lcd_EN(1);
	delay_nms(1);
    Lcd_EN(0);
	delay_nus(100);
}
/******************************设置位置*********************************************/		
Lcd_Pos(BYTE strPos)
{                          //设定显示位置
    Lcd_Wcmd(strPos | 0x80);
}

/******************************写数据***********************************************/	
Lcd_Wdat(vu16 nDat) 
{                          
	//while(Lcd_Busy());
    Lcd_EN(0); 
	delay_nus(1);
	Lcd_RS(1); //写入字符显示数据到Lcd
    delay_nus(1);
	Lcd_RW(0); 
	delay_nus(1);

	GPIOB->BSRR = nDat<<8 & 0xff00;    //P0 = dat
	GPIOB->BRR = ((~nDat)<<8) & 0xff00;
	delay_nus(500);

	Lcd_EN(1);
	delay_nms(1);	//延时300us以上才可以
    Lcd_EN(0);
	delay_nus(100);
}

/******************************写字符串***********************************************/
Lcd_String(BYTE *strWrite)
{
	BYTE i;
	i = 0;
	while(strWrite[i] != '\0')
	{                          
		Lcd_Wdat(strWrite[i]);
		i++;
	}
}
Lcd_Init()
{                            //Lcd初始化设定
	delay_nms(15);
	Lcd_Wcmd(0x38);          //16*2显示,5*7点阵,8位数据
    delay_nms(5);
	Lcd_Wcmd(0x08);
	delay_nms(5);
	Lcd_Wcmd(0x0c);          //显示开,关光标
	delay_nms(5);
	//Lcd_Wcmd(0x06);        //移动光标
	delay_nms(5);
	Lcd_Wcmd(0x01);          //清除Lcd的显示内容
    delay_nms(5);
}
Test_GPIOA(vu16 *GPIOATemp)
{
	GPIO_Write(GPIOA, *GPIOATemp);	   
	*GPIOATemp = *GPIOATemp << 1;
	if(*GPIOATemp > 256)
	{
	    *GPIOATemp = 0x0001;
	}
	while(0);
	delay_nms(1000);
	while(0); 
}

	
main()
{
	vu16 GPIOAInitStatus = 0x0001;

#ifdef DEBUG
debug();  //在线调试使用
#endif
	
	RCC_Configuration();      //系统时钟配置函数 
	
	NVIC_Configuration();     //NVIC配置函数 
    //启动GPIO模块时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB1Periph_TIM2 | RCC_APB2Periph_AFIO, ENABLE);
	//把调试设置普通IO口
	//GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);  
	                        
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;          //所有GPIO为同一类型端口
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 //输出的最大频率为50HZ
	GPIO_Init(GPIOA, &GPIO_InitStructure);   //初始化GPIOA端口
	GPIO_Init(GPIOB, &GPIO_InitStructure);   //初始化GPIOB端口

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 
	                              | GPIO_Pin_2 | GPIO_Pin_1 | GPIO_Pin_0; //所有GPIO为同一类型端口
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 //输出的最大频率为50HZ
	GPIO_Init(GPIOB, &GPIO_InitStructure);               //初始化GPIOB端口

	GPIO_Write(GPIOA,0x0000);  //将GPIOA 16个端口全部置为低电平
	GPIO_Write(GPIOB,0xffff);  //将GPIOB 16个端口全部置为高电平

	Lcd_Init();               // 初始化Lcd

	Lcd_Pos(0);                //设置显示位置为第一行的第1个字符
	Lcd_String(dis1);
	Lcd_Pos(0x40);             //设置显示位置为第二行的第1个字符
	Lcd_String(dis2);	

    while(1)              
    { 	    		
        Test_GPIOA(&GPIOAInitStatus);
	}
}

/*******************************************************************************
*                           配置RCC
*******************************************************************************/
void RCC_Configuration(void)
{   
 //复位RCC外部设备寄存器到默认值
  RCC_DeInit();

  //打开外部高速晶振
  RCC_HSEConfig(RCC_HSE_ON);

  //等待外部高速时钟准备好
  HSEStartUpStatus = RCC_WaitForHSEStartUp();

  if(HSEStartUpStatus == SUCCESS)   //外部高速时钟已经准别好
  {								    
    //开启FLASH的预取功能
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    //FLASH延迟2个周期
    FLASH_SetLatency(FLASH_Latency_2);
 	
    //配置AHB(HCLK)时钟=SYSCLK
    RCC_HCLKConfig(RCC_SYSCLK_Div1);  
  
    //配置APB2(PCLK2)钟=AHB时钟
    RCC_PCLK2Config(RCC_HCLK_Div1); 

    //配置APB1(PCLK1)钟=AHB 1/2时钟
    RCC_PCLK1Config(RCC_HCLK_Div2);

    //配置PLL时钟 == 外部高速晶体时钟*9  PLLCLK = 8MHz * 9 = 72 MHz 
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);

    //使能PLL时钟
    RCC_PLLCmd(ENABLE);

    //等待PLL时钟就绪
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    //配置系统时钟 = PLL时钟
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    //检查PLL时钟是否作为系统时钟
    while(RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }
}


/*******************************************************************************
*                             NVIC配置函数
*******************************************************************************/
void NVIC_Configuration(void)
{
 NVIC_InitTypeDef NVIC_InitStructure;
#ifdef  VECT_TAB_RAM  
  /* Set the Vector Table base location at 0x20000000 */ 
  NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); 
#else  /* VECT_TAB_FLASH  */
  /* Set the Vector Table base location at 0x08000000 */ 
  NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);   
#endif

  
  /* 开启定时器2 */
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}



#ifdef  DEBUG
/*******************************************************************************
* Function Name  : assert_failed
* Description    : Reports the name of the source file and the source line number
*                  where the assert_param error has occurred.
* Input          : - file: pointer to the source file name
*                  - line: assert_param error line source number
* Output         : None
* Return         : None
*******************************************************************************/
void assert_failed(u8* file, u32 line)
{ 
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* InfInite loop */
  while (1)
  {
  }
}
#endif

注意:

(1)STM32驱动1602没法显示的原因之一:GPIO口直接连接1602,需要用10k的电阻上拉。

(2)写指令和写数据,差别仅仅在于RS的电平不一样而已,即引脚RS是数据和命令选择端,S应该是Select的缩写,输入参数的DATA,要么是命令,要么是数据。

(3)RW是R/W的缩写,斜杆也是选择的意思,读即R(高电平有效),写即W(低电平有效)

(4)要将E引脚置为低电平,为下一次E的高脉冲做准备,这称为释放时钟线。

(5)时序图读法:从上到下,从左到右,高电平在上,低电平在下,高阻态在中间。双线表示可能高也可能低,视数据而定。交叉线表示状态的高低变化点,可以是高变低,也可以是低变高,也可以不变。竖线是生命线,代表时序图的对象在一段时期内的存在,时序图中每个对象和底部中心都有一条垂直段的虚线,这就是对象的生命线,对象的消息存在于两条生命线之间。

(6)读写时序图之前一般需要初始化,而初始化有可能模式选择,引脚电平初始化等。

相关链接:http://hi.baidu.com/houyulin016/item/114631c0d9d3a72647d5c05f


你可能感兴趣的:(单片机)