C语言 阅读STM32 L476 RG上的PS/2键盘扫描代码

fzsnzjdm  于 2022-12-02  发布在  其他
关注(0)|答案(1)|浏览(164)

I am trying to read scan codes from a PS/2 keyboard on an STM32 L476RG (NUCLEO L476RG dev board).

PS/2 and USART:

I believe that I can use the USART hardware on the STM32 to read PS/2 data, if there is a reason why not then please help me understand. From my reading and observations with a scope, the PS/2 protocol sends the following packet format on the data line:

  1. 1 start bit. This is always 0.
  2. 8 data bits, least significant bit first.
  3. 1 parity bit (odd parity).
  4. 1 stop bit. This is always 1.
    Each bit is captured on the falling edge of a separate clock signal that idles high. The clock on my particular keyboard sits at around 13.1 kHz.
    Both systems will output at 3.3V from the keyboard if given 3.3V power, a a scope trace shoes that these waveforms are just fine while connected to the STM32.

What's Going Wrong

Basically, the system is not generating an interrupt when a key is pressed. The RXNE (receive buffer not empty flag) stays low and no interrupts are generated.
I go into more detail below, but if I use auto baud rate in "0x55" mode, an interrupt is triggered, but only because the auto baud rate error flag is set. All other modes do nothing (no interrupts).

What I've Tried

I use the following code to initialize USART1 on the STM32 to read from the keyboard:

void init_keyboard(uint32_t baud)
{
// Enable clock to GPIOA
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;

// Enable clock to USART1
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;

// Configure UART
USART1->CR1 |= USART_CR1_RE  |   // Receiver enabled
               USART_CR1_PCE |   // Parity control enabled
               USART_CR1_PS  |   // Odd parity
               USART_CR1_M0  |   // 8 data bits
               USART_CR1_RXNEIE; // Enable RX interrupt

USART1->CR1 &= ~(USART_CR1_TE);

USART1->CR2 |= USART_CR2_CLKEN     | // Enable USART clock (sync mode)
               USART_CR2_ABREN     | // Enable auto Baud Rate
               USART_CR2_CPOL;       // Clock polarit 1 (idle high)

// The following need to be cleared for sync mode
USART1->CR2 &= ~(USART_CR2_LINEN);
USART1->CR3 &= ~(USART_CR3_SCEN  |
                 USART_CR3_HDSEL |
                 USART_CR3_IREN);

USART1->BRR |= (SystemCoreClock / baud); // set baud rate

USART1->CR1 |= USART_CR1_UE;

NVIC->ISER[1] |= (1 << (USART1_IRQn & 0x1F));

// Configure AF pins 8 and 10 to AF7 (USART)
GPIOA->AFR[1]  |= (GPIO_AF7_USART1 << 0);
GPIOA->AFR[1]  |= (GPIO_AF7_USART1 << 8);

// Configure PA8 (CK) and PA10 (RX) in alt. func. mode
GPIOA->MODER &= ~(GPIO_MODER_MODE8   | GPIO_MODER_MODE10);
GPIOA->MODER |=  (GPIO_MODER_MODE8_1 | GPIO_MODER_MODE10_1);

// Enable Global Interrupts
__enable_irq();
}

The data bits are LSB first by default, the capture is on the first edge (falling edge) by default and I confirmed that this baud rate / alternate function code works on a standard UART connection at 9600 baud.
I am also trying to use the auto baud rate detect since the baud rate of the keyboard seems to drift throughout the day (12.9 - 13.2 kHz) and I don't trust the baud rate to be stable. I tried with and without the auto baud rate and I tried auto baud rate in all 4 modes: (start bit measurement, falling-to-falling measurement, 0x55 frame detection, 0x7F frame detection). The only one that gives me anything is 0x7F, which throws an auto baud rate error whenever I read a code other than 0x55 (press a key besides '+'), but does nothing when I send a frame with 0x55 (press '+' key).
And just in case you're wondering, I did call the function in main. :)

/* ISR for handling keypresses */
void USART1_IRQHandler(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  init_keyboard(13120);

  while (1) {/* wait for interrupts */ }
}

void USART1_IRQHandler(void)
{
    if (USART1->ISR & USART_ISR_RXNE)
    {
        char read_byte = USART1->RDR;
        while (1) {/* catch here for debugging */ }
    }
}
a2mppw5e

a2mppw5e1#

感谢Flexz发现以下问题:
STM32 L476缺少CR2寄存器中的同步从机描述部分和SLVEN位,而STM32 L4+系列中存在这些部分和位,即L476只能在USART同步模式下作为主机。
这意味着我不能从PS/2设备作为从机读取,至少不能使用时钟。我***可以,***但是仍然异步地从RX端口读取。这意味着我 * 不 * 想在USARTx-〉CR2中使能CLKEN。
如果我异步处理线路,那么我可以使用模式00中的自动波特率来捕获数据,即使波特率漂移,就像在这个键盘上一样。

最终代码:

void init_keyboard(uint32_t baud)
{
    //clear_buffer();

    // Enable clock to GPIOA
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;

    // Enable clock to USART1
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;

    // Configure UART
    USART1->CR1 |= USART_CR1_RE  |   // Receiver enabled
                   USART_CR1_PCE |   // Parity control enabled
                   USART_CR1_PS  |   // Odd parity
                   USART_CR1_M0  |   // 8 data bits
                   USART_CR1_RXNEIE; // Enable RX interrupt

    USART1->CR1 &= ~(USART_CR1_TE);

    USART1->CR2 |= USART_CR2_ABREN;  // Enable auto Baud Rate

    USART1->CR3 |= USART_CR3_OVRDIS; // Disable overrun detection

    USART1->BRR |= (SystemCoreClock / baud);

    USART1->CR1 |= USART_CR1_UE;

    NVIC->ISER[1] |= (1 << (USART1_IRQn & 0x1F));

    // Configure AF pin10 to AF7 (USART)
    GPIOA->AFR[1]  |= (GPIO_AF7_USART1 << 8);

    // Configure PA10 (RX) in alt. func. mode
    GPIOA->MODER &= ~(GPIO_MODER_MODE10);
    GPIOA->MODER |=  (GPIO_MODER_MODE10_1);

    // Enable Global Interrupts
    __enable_irq();
}

相关问题