【接线】
上面: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模式: