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