STM32 四线驱动1602A 填坑!解决重启乱码

xiaoxiao2021-02-28  24

最近学STM32,用来丰富一下生活,一个四线1602搞得一星期,对自己的智商也是醉了。填坑开始!

用的是HAL库编写的,仅仅在ODR寄存器使用了一点寄存器操作,其余全是HAL函数。

硬件接口如下:

RS    PA0         

R/W PA1

EN   PA4

 数据口D4-D7       PB4-PB7

PB11和PB14使用了两个指示灯,用来在while函数里显示系统正在运行;

V0口接一个10K的电位器,连接到板子的GND,用来调节对比度。其实之前自己也在这里跳坑了,明明显示屏已经显示出字符串了,可对比度太高了,一片白,没看出来,所以对着源码调试了好久,做了太多无用功,偶然的一次,钛合金狗眼发作,隐隐约约看出来了文字,心中出了一口长气。

然而好景不长,接着就出现了乱码的问题,只能显示部分,也就是下面的解决方法了。

现在说说里面另外需要填坑的地方,之前乱码出现的问题主要是在LCD初始化的过程中。

在LCD1602_INIT()函数中,用四线时,1602的初始化只需要高四位数据就可以完成,在初始化完成之后必须再传入四位数据,执行完“write_com(0x28);”之后液晶已经初始化,其实在执行了一半的时候就已经初始化完成,此时又传入了四位数据(一个写语句会传入8位数据),这时候如果直接写数据的话,就会形成乱码,所以需要两次功能性初始化。

源代码贴在下面了,

主函数如下:

/* Includes ------------------------------------------------------------------*/ #include "main.h" #include "stm32f1xx_hal.h" #include "gpio.h" #include "1602.h" void SystemClock_Config(void); int main(void) {   unsigned char dat[]={"thank you for your waiting!"};     HAL_Init();    SystemClock_Config();    MX_GPIO_Init();   LCD1602_INIT();     lcd1602_show_string(0,0,dat);     while (1)   {   HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_11); HAL_Delay(1000); HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_14); HAL_Delay(1000);     } } /** System Clock Configuration */ void SystemClock_Config(void) {   RCC_OscInitTypeDef RCC_OscInitStruct;   RCC_ClkInitTypeDef RCC_ClkInitStruct;     /**Initializes the CPU, AHB and APB busses clocks      */   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;   RCC_OscInitStruct.HSEState = RCC_HSE_ON;   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;   RCC_OscInitStruct.HSIState = RCC_HSI_ON;   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)   {     _Error_Handler(__FILE__, __LINE__);   }     /**Initializes the CPU, AHB and APB busses clocks      */   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)   {     _Error_Handler(__FILE__, __LINE__);   }     /**Configure the Systick interrupt time      */   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);     /**Configure the Systick      */   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);   /* SysTick_IRQn interrupt configuration */   HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); } /**   * @brief  This function is executed in case of error occurrence.   * @param  None   * @retval None   */ void _Error_Handler(char * file, int line) {   /* USER CODE BEGIN Error_Handler_Debug */   /* User can add his own implementation to report the HAL error return state */   while(1)    {   }   /* USER CODE END Error_Handler_Debug */  } #ifdef USE_FULL_ASSERT /**    * @brief Reports the name of the source file and the source line number    * where the assert_param error has occurred.    * @param file: pointer to the source file name    * @param line: assert_param error line source number    * @retval None    */ void assert_failed(uint8_t* file, uint32_t line) {   /* USER CODE BEGIN 6 */   /* 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) */   /* USER CODE END 6 */ } #endif /**   * @}   */  /**   * @} */  /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

GPIO设置子程序如下,

#include "gpio.h" void MX_GPIO_Init(void) {   GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */   __HAL_RCC_GPIOD_CLK_ENABLE();   __HAL_RCC_GPIOA_CLK_ENABLE();   __HAL_RCC_GPIOB_CLK_ENABLE();   /*Configure GPIO pin Output Level */   HAL_GPIO_WritePin(GPIOA, R_W_Pin|RS_Pin|EN_Pin, GPIO_PIN_RESET);   /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_0|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11|GPIO_PIN_14, GPIO_PIN_RESET);   /*Configure GPIO pins : PAPin PAPin PAPin */   GPIO_InitStruct.Pin = R_W_Pin|RS_Pin|EN_Pin;   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);   /*Configure GPIO pins : PB4 PB5 PB6 PB7 PB11 PB14*/   GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_11|GPIO_PIN_14;   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;   HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); }

LCD显示模块函数如下,

#include "stm32f1xx_hal.h" #include "1602.h" #include "stm32f103xb.h" //实际测试可用任意IO读写LCD1602//系统时钟72MHz ///4线LCD1602 //函数名:  LCD1602_INIT //函数功能:LCD1602初始化 //说明:用四线时,1602的初始化只需要高四位数据就可以完成,在初始化完成之后必须再传入四位数据,需注意。 //      执行完write_com(0x28);之后液晶已经初始化,其实在执行了一半的时候就已经初始化完成,此时又传入了 //      四位数据(一个写语句会传入8位数据),这时候如果直接写数据的话,就会形成乱码 //注释:    DATA可以是指令或者数据 void LCD1602_INIT(void) { HAL_Delay(200); //等待液晶供电稳定200ms    write_com(0x28);//4位数据总线模式,显示2行数据,5*10点阵每字符 0X38为8位数据模式, 显示2行数据,5*10点阵每字符 HAL_Delay(5); E_H(); E_L(); write_com(0x28);//4位数据总线模式,显示2行数据,5*10点阵每字符 0X38为8位数据模式, 显示2行数据,5*10点阵每字符 HAL_Delay(5); write_com(0x0c); //开显示,有光标,光标闪烁 HAL_Delay(5); write_com(0x06); //写入数据光标右移,写入新数据显示屏不移动 HAL_Delay(5); write_com(0x01); //清屏 HAL_Delay(5); } //函数名:LCD_1602_READY //函数功能:检测PB7是否为高电平,忙碌状态; //注释: void LCD_1602_READY(void) { uint8_t sta; RS_L(); RW_H(); do { E_H(); HAL_Delay(5); sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7); HAL_Delay(5); E_L(); HAL_Delay(5); } while ( sta & 0x80); } //函数名:  LCD1602_Clear_Screen //函数功能:LCD1602清屏 //注释:    void LCD1602_Clear_Screen(void) { LCD1602_DATA_write(Write_order,0x01); //清屏 } //函数名:  write_com //函数功能:LCD1602写指令 //注释:     void write_com(unsigned char dat) {   unsigned char j; LCD_1602_READY();   RS_L();//指令 RW_L();//写操作模式 E_L();   for(j=0;j<2;j++)   { GPIOB->ODR =(dat&0x00F0); E_H(); HAL_Delay(5); E_L();//允许        dat<<=4; HAL_Delay(5);   } } //函数名:  write_data //函数功能:LCD1602写数据 //注释:    void write_data(unsigned char dat) {   unsigned char j; LCD_1602_READY();   RS_H();//数据 RW_L();//写操作模式 E_L();   for(j=0;j<2;j++)   { GPIOB->ODR =(dat&0x00F0); E_H(); HAL_Delay(5);   E_L();      dat<<=4; HAL_Delay(5);   } }

//函数名:lcd1602_write //作用:将数据或者指令写入LCD1602 //注释: void LCD1602_DATA_write(LCD1602_Write_TypeDef order,unsigned char dat) { if(order==Write_data) write_data(dat); else   write_com(dat); } //函数名:lcd1602_show_character //作用:  在制定坐标,显示单个字符 void lcd1602_show_character(unsigned char x,unsigned char y,unsigned char dat) { unsigned char address; x=x; y=y%2; if(y==1) address=0xc0+x; else address=0x80+x;   LCD1602_DATA_write(Write_order,address); LCD1602_DATA_write(Write_data,dat); } //函数名:lcd1602_show_number //作用: //     以(X,Y)坐标为起始,显示一个数字(此数字值不能大于4294967295(0xffffffff)) //注释: //     返回数字的显示长度,若改行显示不下,函数自动换行, 显示其余部分 unsigned char lcd1602_show_number(unsigned char x,unsigned char y,unsigned short dat) { unsigned short pow=1,instead; unsigned char increase=0,lengh=0; instead=dat; while(instead!=0) { instead=instead/10; increase++; } lengh=increase; if(increase==0) { lcd1602_show_character(x,y,0x30); return 1; } else { for(;increase>1;increase--) pow=pow*10; while(pow!=0) { instead=dat/pow; lcd1602_show_character(x,y,(0x30+instead)); x++; if(((x)==0)&&(x!=0)) { y++; y=y%2; x=x; } dat=dat%pow; pow=pow/10;     } } return lengh; } //函数名:lcd1602_show_string //作用: //     以(X,Y)坐标为起始,显示一个字符串 //注释: //     返回数字的显示长度,若改行显示不下,函数自动换行, 显示其余部分,字符长度小于256 unsigned char lcd1602_show_string(unsigned char x,unsigned char y,unsigned char *dat) { unsigned char lengh=0; while(dat[lengh]!='\0') { if(((x)==0)&&(x!=0)) {     y++; y=y%2;     x=x; }     lcd1602_show_character(x,y,dat[lengh]); x++; lengh++;  } return (lengh); } //函数名:lcd1602_show_number //作用: //     以(X,Y)坐标为起始,显示一个数字(此数字值不能大于4294967295(0xffffffff)) //注释: //     返回数字的显示长度,若改行显示不下,函数自动换行, 显示其余部分 unsigned char lcd1602_show_s32(unsigned char x,unsigned char y,short dat) { short pow=1,instead; unsigned char increase=0,lengh=0; if(((dat&0x80000000)==0x80000000)&&(dat!=0xffffffff))//负数 {   instead=-dat;   y=y%2;   lcd1602_show_character(x,y,0x2D);//"-"   x++; } else if((dat&0x80000000)!=0x80000000)//正数 {   instead=dat;   y=y%2;   lcd1602_show_character(x,y,0X2B);//"+"   x++;      } else//0 {   instead=0;    } dat=instead;  while(instead!=0) { instead=instead/10; increase++; } lengh=increase; if(increase==0) { lcd1602_show_character(x,y,0x30); return 1; } else { for(;increase>1;increase--) pow=pow*10; while(pow!=0) { instead=dat/pow; lcd1602_show_character(x,y,(0x30+instead)); x++; if(((x)==0)&&(x!=0)) { y++; y=y%2; //x=x; } dat=dat%pow; pow=pow/10;     } } return lengh+1; } LCD头文件就是定义了上面的函数:

#ifndef __1602_H__ #define __1602_H__ typedef enum { Write_data  =0X00, Write_order =0x01/*BIT0*/  }LCD1602_Write_TypeDef; #define RW_L() GPIOA->ODR &=~(0x01<<1)//RW=0,PA1 #define RW_H() GPIOA->ODR |=(0x01<<1)//RW=1 #define RS_L() GPIOA->ODR &=~(0x01)//RS=0,PA0 #define RS_H() GPIOA->ODR |=(0x01)//RS=1 #define E_L()  GPIOA->ODR &=~(0x01<<4)//E=0,PA4 #define E_H()  GPIOA->ODR |=(0x01<<4)//E=1 //函数名:  LCD1602_INIT //函数功能:LCD1602初始化 //注释:    DATA可以是指令或者数据 void LCD1602_INIT(void); //函数名:  LCD1602_Clear_Screen //函数功能:LCD1602清屏 //注释:    void LCD1602_Clear_Screen(void); //函数名:  write_com //函数功能:LCD1602写指令 //注释:     void write_com(unsigned char dat); //函数名:  write_data //函数功能:LCD1602写数据 //注释:    void write_data(unsigned char dat); //函数名:lcd1602_write //作用:将数据或者指令写入LCD1602 //注释: void LCD1602_DATA_write(LCD1602_Write_TypeDef order,unsigned char dat); //函数名:lcd1602_show_character //作用:  在制定坐标,显示单个字符 void lcd1602_show_character(unsigned char x,unsigned char y,unsigned char dat); //函数名:lcd1602_show_number //作用: //     以(X,Y)坐标为起始,显示一个数字(此数字值不能大于4294967295(0xffffffff)) //注释: //     返回数字的显示长度,若改行显示不下,函数自动换行, 显示其余部分 unsigned char lcd1602_show_number(unsigned char x,unsigned char y,unsigned short dat); //函数名:lcd1602_show_string //作用: //     以(X,Y)坐标为起始,显示一个字符串 //注释: //     返回数字的显示长度,若改行显示不下,函数自动换行, 显示其余部分,字符长度小于256 unsigned char lcd1602_show_string(unsigned char x,unsigned char y,unsigned char *dat); //显示有符号S32 unsigned char lcd1602_show_s32(unsigned char x,unsigned char y,short dat); //为了适应任意IO驱动LCD1602做的定义 //读忙操作 void LCD_1602_READY(void); //配置GPIO端口 void GPIO_Configuration(void); #endif

转载请注明原文地址: https://www.6miu.com/read-1750105.html

最新回复(0)