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