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