STM32F407VE单片机使用I2C接口操作内部控制芯片为RA8816的FYD12864-1001A型的12864液晶



【接线】

上面:GND, VCC=5V, CS=高电平, SDA=PB7, SCK=PB6, RST=PA1

下面:WR=RD=DB2=DB4=DB5=GND(可自定义), DB6=GND, D/C=高电平

其余引脚悬空!

DB3必须悬空!因为在电路板上DB3是和CS相连的,且CS接的是高电平,所以该位地址始终为1,不能修改

特别注意,D/C必须要接高电平(3V或5V均可),否则有时上电后屏幕无法显示文字!


I2C_ADDR  7  6       5     4        3        2   1       0
引脚              0  WR  RD  DB5  DB4  1   DB2  0
最终地址      0  0      0      0        0       1    0       0       ->      0x04

地址引脚上的电平改变后,I2C地址立即改变。

【跳线配置】

用焊锡短接JPS, JP4B, JP68, JPSCS, JPS3L

断开以下跳线:JPS3H, JP80, JPP, JP8B

JP8B是默认焊接上的,选I2C模式时必须断开该跳线,否则上电就会发生短路!

JPA和JPK跳线决定背光是否使用与模块相同的电源。



判断某个引脚是否为悬空状态的方法:
将万用表调到测电压模式,将表笔
1. 将表笔连到GND与被测引脚之间,0V
2. 将表笔连到VCC与被测引脚之间,0V
3. 重新连到GND与被测引脚之间,示数为2.3V
则证明该引脚为悬空状态。
如果被测引脚与GND或VCC之间的电压始终固定,则表明引脚不为悬空状态,不可以用杜邦线直接连到VCC或GND,否则有短路的危险。

DB3在电路板上是和CS引脚连通的,由于选I2C模式时CS必须为高电平,所以DB3固定为高电平,不可外接杜邦线。

DB6=DB7=0时液晶屏使用I2C模式,由于DB7在液晶背面有JPS3L跳线,所以可悬空。DB6与JPS3H跳线相连,JPS3H短接时DB6为高电平,不是I2C模式,所以不能短接JPS3H,并且要用杜邦线把DB6接到GND。


该液晶使用的控制芯片是RA8816,在网上很容易搜索到datasheet。

【测试程序】

main.c:

#include <stdio.h>
#include <stm32f4xx.h>
#include "FYD12864.h"

#define FYD12864 ((FILE *)3)

void delay(uint16_t nms)
{
  TIM_TimeBaseInitTypeDef tim;
  TIM_SelectOnePulseMode(TIM6, TIM_OPMode_Single);
  TIM_UpdateRequestConfig(TIM6, TIM_UpdateSource_Regular);
  
  tim.TIM_ClockDivision = TIM_CKD_DIV1;
  tim.TIM_CounterMode = TIM_CounterMode_Up;
  tim.TIM_Period = 10 * nms - 1;
  tim.TIM_Prescaler = 8399;
  TIM_TimeBaseInit(TIM6, &tim);
  TIM_Cmd(TIM6, ENABLE);
  
  while (TIM_GetFlagStatus(TIM6, TIM_FLAG_Update) == RESET);
  TIM_ClearFlag(TIM6, TIM_FLAG_Update);
}

int fputc(int ch, FILE *fp)
{
  if (fp == stdout)
  {
    if (ch == '\n')
    {
      while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
      USART_SendData(USART1, '\r');
    }
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, ch);
  }
  else if (fp == FYD12864)
  {
    if (ch == '\n')
      FYD12864_NewLine();
    else
      FYD12864_Write(FYD12864_RAMD, ch);
  }
  return ch;
}

void show_clock(void)
{
  RCC_ClocksTypeDef clocks;
  RCC_GetClocksFreq(&clocks);
  printf("STM32F407VE\n");
  printf("SYSCLK=%.2fMHz HCLK=%.2fMHz PCLK1=%.2fMHz PCLK2=%.2fMHz\n", clocks.SYSCLK_Frequency / 1000000.0, clocks.HCLK_Frequency / 1000000.0, clocks.PCLK1_Frequency / 1000000.0, clocks.PCLK2_Frequency / 1000000.0);
}

// 显示I2C有应答的从器件地址
uint8_t get_i2c_addr(void)
{
  uint8_t addr;
  uint8_t acked = 0;
  for (addr = 0x02; addr <= 0xfc; addr += 2)
  {
    I2C_GenerateSTART(I2C1, ENABLE);
    while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
    I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter);
    while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR && I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET);
    if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET)
    {
      acked = addr;
      printf("0x%02x: ACK\n", addr);
    }
    else
    {
      I2C_ClearFlag(I2C1, I2C_FLAG_AF);
      //printf("0x%02x: NACK\n", addr);
    }
    I2C_GenerateSTOP(I2C1, ENABLE);
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
  }
  if (acked == 0)
    printf("No slave!\n");
  return acked;
}

int main(void)
{
  GPIO_InitTypeDef gpio;
  I2C_InitTypeDef i2c;
  USART_InitTypeDef usart;
  
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 | RCC_APB1Periph_TIM6, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  
  gpio.GPIO_Mode = GPIO_Mode_OUT;
  gpio.GPIO_OType = GPIO_OType_PP;
  gpio.GPIO_Pin = GPIO_Pin_1;
  gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
  gpio.GPIO_Speed = GPIO_Low_Speed;
  GPIO_Init(GPIOA, &gpio);
  
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
  gpio.GPIO_Mode = GPIO_Mode_AF;
  gpio.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
  gpio.GPIO_Speed = GPIO_High_Speed;
  GPIO_Init(GPIOA, &gpio);
  
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
  gpio.GPIO_OType = GPIO_OType_OD;
  gpio.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  gpio.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(GPIOB, &gpio);
  
  usart.USART_BaudRate = 115200;
  usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  usart.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
  usart.USART_Parity = USART_Parity_No;
  usart.USART_StopBits = USART_StopBits_1;
  usart.USART_WordLength = USART_WordLength_8b;
  USART_Init(USART1, &usart);
  USART_Cmd(USART1, ENABLE);
  
  i2c.I2C_Ack = I2C_Ack_Disable;
  i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  i2c.I2C_ClockSpeed = 10000; // 速度不能太快, 否则会乱码
  i2c.I2C_DutyCycle = I2C_DutyCycle_2;
  i2c.I2C_Mode = I2C_Mode_I2C;
  i2c.I2C_OwnAddress1 = 0;
  I2C_Init(I2C1, &i2c);
  I2C_Cmd(I2C1, ENABLE);
  
  show_clock();
  //get_i2c_addr();
  
  FYD12864_Init();
  FYD12864_WriteString("STM32F407VE型号"); // I2C方式批量写入, 不支持\n
  FYD12864_NewLine(); // 换行
  FYD12864_WriteString("意法半导体单片机");
  fprintf(FYD12864, "微控制器\n波特率:%d", usart.USART_BaudRate); // I2C一个一个字符写入, 支持\n
  while (1)
    __WFI();
}
FYD12864.h:
#define FYD12864_ADDR 0x04

#define FYD12864_DWFR 0x00

#define FYD12864_PWRR 0x01
#define FYD12864_PWRR_SRST 0x80
#define FYD12864_PWRR_MCLR 0x40
#define FYD12864_PWRR_DOFF_Z 0x02

#define FYD12864_SYSR 0x02
#define FYD12864_SYSR_LS 0xf0
#define FYD12864_SYSR_LS_Pos 4
#define FYD12864_SYSR_GB_EN 0x08
#define FYD12864_SYSR_RS 0x03
#define FYD12864_SYSR_RS_Pos 0

#define FYD12864_MWMR 0x03
#define FYD12864_MWMR_MD 0x03
#define FYD12864_MWMR_MD_Pos 0

#define FYD12864_XCUR 0x05
#define FYD12864_XCUR_X 0x3f
#define FYD12864_YCUR 0x06
#define FYD12864_YCUR_Y 0x7f

#define FYD12864_ISR 0x0f // Interrupt Status
#define FYD12864_ISR_BF 0x80 // Busy Flag
#define FYD12864_ISR_IO_I 0x08 // I/O Port Interrupt
#define FYD12864_ISR_SCR_I 0x04 // Scroll interrupt
#define FYD12864_ISR_KI 0x02 // Key-scan interrupt
#define FYD12864_ISR_BI 0x01 // Busy Interrupt

#define FYD12864_CSTR 0x10 // Contrast Adjust Register
#define FYD12864_CSTR_BR 0xe0 // Bias
#define FYD12864_CSTR_BR_Pos 5
#define FYD12864_CSTR_CT 0x1f // Contrast
#define FYD12864_CSTR_CT_Pos 0

#define FYD12864_DRCRA 0x11 // Driver Control Register1
#define FYD12864_DRCRA_BOFF 0x80 // Booster control
#define FYD12864_DRCRA_EN_R 0x40 // Reference voltage control
#define FYD12864_DRCRA_EN_G 0x20 // V0 control
#define FYD12864_DRCRA_ROFF 0x10 // Voltage Follower control
#define FYD12864_DRCRA_IDIR 0x08 // Icon sequence select
#define FYD12864_DRCRA_CDIR 0x02 // Common sequency select
#define FYD12864_DRCRA_SDIR 0x01 // Segment sequency select

#define FYD12864_DRCRB 0x12 // Driver Control Register2
#define FYD12864_DRCRB_CK_BS 0xc0 // clock of Booster
#define FYD12864_DRCRB_CK_BS_Pos 6
#define FYD12864_DRCRB_RR 0x38 // Resistor Ratio of Regulator
#define FYD12864_DRCRB_RR_Pos 3
#define FYD12864_DRCRB_IRS 0x04 // resistors for the V0 voltage level adjustment
#define FYD12864_DRCRB_HD 0x03 // LCD driving current
#define FYD12864_DRCRB_HD_Pos 0

#define FYD12864_RAMD 0x80 // Memory Data

typedef enum {FYD12864_Mode_Image = 0, FYD12864_Mode_8x8 = 1, FYD12864_Mode_8x16 = 2, FYD12864_Mode_16x16 = 3} FYD12864_Mode;

void FYD12864_Clear(void);
void FYD12864_Init(void);
void FYD12864_NewLine(void);
uint8_t FYD12864_Read(uint8_t addr);
void FYD12864_Reset(void);
void FYD12864_SetMode(FYD12864_Mode mode);
void FYD12864_SetPos(uint8_t x, uint8_t y);
void FYD12864_Wait(void);
uint8_t FYD12864_Write(uint8_t addr, uint8_t value);
uint8_t FYD12864_WriteData(const void *data, uint8_t len);
uint8_t FYD12864_WriteString(const char *str);
FYD12864.c:
#include <stdio.h>
#include <stm32f4xx.h>
#include <string.h>
#include "FYD12864.h"

void delay(uint16_t nms);

void FYD12864_Clear(void)
{
  FYD12864_Write(FYD12864_PWRR, FYD12864_Read(FYD12864_PWRR) | FYD12864_PWRR_MCLR);
  FYD12864_Wait();
}

void FYD12864_Init(void)
{
  FYD12864_Reset(); // 复位
  FYD12864_Write(FYD12864_SYSR, (7 << FYD12864_SYSR_LS_Pos) | FYD12864_SYSR_GB_EN | FYD12864_SYSR_RS); // 128x64, GB Code
  FYD12864_SetMode(FYD12864_Mode_16x16); // 16x16字型模式
  FYD12864_Write(FYD12864_CSTR, (4 << FYD12864_CSTR_BR_Pos) | (23 << FYD12864_CSTR_CT_Pos)); // 设置对比度
  FYD12864_Write(FYD12864_DRCRA, FYD12864_DRCRA_BOFF | FYD12864_DRCRA_EN_R | FYD12864_DRCRA_EN_G | FYD12864_DRCRA_ROFF);
  FYD12864_Write(FYD12864_DRCRB, FYD12864_DRCRB_CK_BS | (6 << FYD12864_DRCRB_RR_Pos) | FYD12864_DRCRB_IRS | FYD12864_DRCRB_HD); // 12.5kHz, X6, 大电流
  FYD12864_Write(FYD12864_PWRR, FYD12864_PWRR_DOFF_Z); // 打开显示功能
  FYD12864_Clear(); // 清屏
}

// 换行
void FYD12864_NewLine(void)
{
  FYD12864_SetPos(0, FYD12864_Read(FYD12864_YCUR) + 16);
}

uint8_t FYD12864_Read(uint8_t addr)
{
  I2C_GenerateSTART(I2C1, ENABLE);
  while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
  I2C_Send7bitAddress(I2C1, FYD12864_ADDR, I2C_Direction_Transmitter);
  while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR && I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET);
  if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET)
  {
    I2C_SendData(I2C1, addr);
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF) == RESET);
    
    I2C_GenerateSTART(I2C1, ENABLE);
    while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
    I2C_Send7bitAddress(I2C1, FYD12864_ADDR, I2C_Direction_Receiver);
    while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR);
    
    I2C_GenerateSTOP(I2C1, ENABLE);
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
    return I2C_ReceiveData(I2C1);
  }
  else
  {
    I2C_ClearFlag(I2C1, I2C_FLAG_AF);
    I2C_GenerateSTOP(I2C1, ENABLE);
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
    printf("No ack!\n");
    return 0;
  }
}

void FYD12864_Reset(void)
{
  GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
  delay(30);
  GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
  delay(150);
}

void FYD12864_SetMode(FYD12864_Mode mode)
{
  FYD12864_Write(FYD12864_MWMR, (FYD12864_Read(FYD12864_MWMR) & ~FYD12864_MWMR_MD) | (mode << FYD12864_MWMR_MD_Pos));
}

void FYD12864_SetPos(uint8_t x, uint8_t y)
{
  FYD12864_Write(FYD12864_XCUR, x & FYD12864_XCUR_X);
  FYD12864_Write(FYD12864_YCUR, y & FYD12864_YCUR_Y);
}

void FYD12864_Wait(void)
{
  while (FYD12864_Read(FYD12864_ISR) & FYD12864_ISR_BF);
}

uint8_t FYD12864_Write(uint8_t addr, uint8_t value)
{
  uint8_t ret = 0;
  I2C_GenerateSTART(I2C1, ENABLE);
  while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
  I2C_Send7bitAddress(I2C1, FYD12864_ADDR, I2C_Direction_Transmitter);
  while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR && I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET);
  if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET)
  {
    ret = 1;
    I2C_SendData(I2C1, addr);
    I2C_SendData(I2C1, value);
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF) == RESET);
  }
  else
  {
    I2C_ClearFlag(I2C1, I2C_FLAG_AF);
    printf("No ack!\n");
  }
  I2C_GenerateSTOP(I2C1, ENABLE);
  while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
  return ret;
}

// 此函数没有对AF, ARLO, BERR等错误进行处理, 所以请不要在短时间内发送大量的数据!
uint8_t FYD12864_WriteData(const void *data, uint8_t len)
{
  const uint8_t *p = data;
  uint8_t ret = 0;
  
  I2C_GenerateSTART(I2C1, ENABLE);
  while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
  I2C_Send7bitAddress(I2C1, FYD12864_ADDR, I2C_Direction_Transmitter);
  while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR && I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET);
  if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET)
  {
    ret = 1;
    I2C_SendData(I2C1, FYD12864_RAMD);
    while (len--)
    {
      while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET);
      I2C_SendData(I2C1, *p++);
    }
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF) == RESET);
  }
  else
  {
    I2C_ClearFlag(I2C1, I2C_FLAG_AF);
    printf("No ack!\n");
  }
  I2C_GenerateSTOP(I2C1, ENABLE);
  while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
  return ret;
}

uint8_t FYD12864_WriteString(const char *str)
{
  return FYD12864_WriteData(str, strlen(str));
}

上电时屏幕不显示内容,按下STM32复位键后才能显示的解决办法:

确保D/C引脚已接到高电平。断开JPA跳线,JA引脚接9012三极管的集电极,基极通过一个10kΩ的电阻接到单片机PB7端口上,发射极接+5V。通电时先暂不打开背光,等初始化完毕了再开背光,PB7设为开漏输出模式,PB7=0时点亮背光。

// STM32F103ZE单片机
// int main(void):
GPIO_WriteBit(GPIOB, GPIO_Pin_7, Bit_SET); // 关背光
gpio.GPIO_Mode = GPIO_Mode_Out_OD;
gpio.GPIO_Pin = GPIO_Pin_7;
gpio.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &gpio);

void FYD12864_Init(void)
{
  FYD12864_Reset(); // 复位
  FYD12864_Write(FYD12864_SYSR, (7 << FYD12864_SYSR_LS_Pos) | FYD12864_SYSR_GB_EN | FYD12864_SYSR_RS); // 128x64, GB Code
  FYD12864_SetMode(FYD12864_Mode_16x16); // 16x16字型模式
  FYD12864_Write(FYD12864_CSTR, (4 << FYD12864_CSTR_BR_Pos) | (23 << FYD12864_CSTR_CT_Pos)); // 设置对比度
  FYD12864_Write(FYD12864_DRCRA, FYD12864_DRCRA_BOFF | FYD12864_DRCRA_EN_R | FYD12864_DRCRA_EN_G | FYD12864_DRCRA_ROFF);
  FYD12864_Write(FYD12864_DRCRB, FYD12864_DRCRB_CK_BS | (6 << FYD12864_DRCRB_RR_Pos) | FYD12864_DRCRB_IRS | FYD12864_DRCRB_HD); // 12.5kHz, X6, 大电流
  FYD12864_Write(FYD12864_PWRR, FYD12864_PWRR_DOFF_Z); // 打开显示功能
  FYD12864_Clear(); // 清屏
  GPIO_WriteBit(GPIOB, GPIO_Pin_7, Bit_RESET); // 开背光
}
经测试,问题完美解决:

5秒后自动熄灭背光,单片机进入STOP模式:

猜你喜欢

转载自blog.csdn.net/zlk1214/article/details/78451048