ST7789并行接口读ID失败,求D/CX、WRX、RDX正确时序
Hey there, let's break down your problem with reading the ST7789's ID3 (0xDC command) over 16-bit parallel. I’ve debugged similar ST77xx parallel interface issues before, so let’s start with the correct timing sequence you’re missing, then fix your code gaps.
First: The Correct D/CX (RS), WRX (nWR), RDX (nRD) Sequence for Read ID3
ST7789’s 16-bit parallel mode has strict timing rules for read operations. Here’s the step-by-step flow for Read ID3:
1. Send the 0xDC Command (Instruction Phase)
First, you need to tell the chip you want to read its ID—this critical step is missing from your current code!
- Pull D/CX (RS) LOW: This signals the chip you’re sending an instruction, not data.
- Keep RDX (nRD) HIGH: We don’t want to trigger a read yet.
- Set your parallel data bus (DB1-DB16) to output mode, and drive the command
0xDConto the bus (for 16-bit mode, the upper 8 bits can be 0 since this is an 8-bit instruction). - Pull WRX (nWR) LOW to trigger the write, hold it for at least the chip’s minimum low-time (a single
__NOP()is usually enough for most MCUs). - Pull WRX (nWR) HIGH to complete the instruction write.
2. Read the ID Value (Data Phase)
Now that the chip knows to send the ID, switch to reading mode:
- Pull D/CX (RS) HIGH: Now we’re reading data, not instructions.
- Keep WRX (nWR) HIGH: No write operations will happen here.
- Switch your parallel data bus (DB1-DB16) to input mode.
- Pull RDX (nRD) LOW: This tells the chip to drive the ID value onto the data bus.
- Wait a tiny bit (another
__NOP()) to let the data stabilize, then read the bus value. - Pull RDX (nRD) HIGH to end the read operation.
Quick note: Some ST7789 variants return a "dummy byte" (like 0x00) first before the real ID, so reading twice like you did is smart—just make sure the timing is right for both reads.
What’s Wrong With Your Current Code?
Looking at your snippet, there are a few critical gaps:
- No instruction write: You jump straight to reading without sending the
0xDCcommand. The chip has no idea you want the ID, so it’s just driving random or default levels on the bus. - Incorrect D/CX state: You start with
set_rs(D/CX HIGH), but you need it LOW when sending the instruction. - RDX timing order: You pull RDX LOW before switching the GPIO to input—this means you’re triggering a read before the bus is ready to receive data, leading to garbage values.
Revised Code Framework
Here’s a corrected version of your code with the proper sequence:
GPIO_InitTypeDef GPIO_InitStructure; // -------------------------- Step 1: Send 0xDC Instruction -------------------------- // Set data bus to OUTPUT mode first // DB1-DB8 <-> PC1-PC8, DB10-DB12 <-> PC10-PC12 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(GPIOC, &GPIO_InitStructure); // DB13 <-> PD2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &GPIO_InitStructure); // DB14 <-> PB9, DB15 <-> PB10, DB16 <-> PB1, DB17 <-> PB2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_9 | GPIO_Pin_10; GPIO_Init(GPIOB, &GPIO_InitStructure); // Set control signals for instruction write clr_rs; // D/CX LOW (instruction mode) set_nw; // WRX HIGH (idle) set_nrd; // RDX HIGH (idle) // Drive 0xDC onto the data bus (adjust shifts based on your pin mapping) // Assuming PC1 = DB1 (LSB), so shift 0xDC left by 1 to map to PC1-PC8 GPIOC->ODR &= ~0x0FF0; // Clear PC1-PC8 GPIOC->ODR |= (0xDC << 1); // Set upper 8 bits (DB9-DB16) to 0 (irrelevant for 8-bit instruction) GPIOB->ODR &= ~(GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_9 | GPIO_Pin_10); GPIOD->ODR &= ~GPIO_Pin_2; // Trigger write: pull WRX LOW, then HIGH clr_nw; __NOP(); // Short delay to meet setup time set_nw; // -------------------------- Step 2: Read ID Data -------------------------- // Switch data bus to INPUT mode GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // Re-configure GPIOC GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_Init(GPIOC, &GPIO_InitStructure); // Re-configure GPIOD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &GPIO_InitStructure); // Re-configure GPIOB GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_9 | GPIO_Pin_10; GPIO_Init(GPIOB, &GPIO_InitStructure); // Set control signals for data read set_rs; // D/CX HIGH (data mode) set_nw; // WRX HIGH (idle) // First read (dummy byte, may be 0x00) clr_nrd; __NOP(); // Wait for data to stabilize uint16_t dummy_data = PORTREAD(); set_nrd; // Second read (real ID, should be 0x52) clr_nrd; __NOP(); uint16_t st7789_id = PORTREAD(); set_nrd; // -------------------------- Step 3: Optional - Restore Bus to Output -------------------------- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // Re-configure all ports back to output if needed // ... (repeat output mode setup code here)
Extra Checks to Rule Out Hardware Issues
- Double-check your pin mapping: You didn’t mention DB9 in your code—make sure it’s either connected to a GPIO (and configured) or properly grounded/floating per your hardware design.
- Verify GPIO input mode: Use floating input (
GPIO_Mode_IN_FLOATING) instead of pull-up/down, since ST7789 uses push-pull outputs. - Confirm reset timing: Before reading the ID, make sure you’ve completed a proper reset of the ST7789 (pull RST LOW, wait, then HIGH) as per the datasheet.
内容的提问来源于stack exchange,提问作者Mehmet Yangın

