Desenvolvimento de Drivers SPI/UART para Chip QCA7000

Configuração das Interfaces de Hardware

Interface SPI (Modo 3)

// Parâmetros da comunicação SPI
#define CONFIG_SPI_CPOL        SPI_POLARITY_HIGH
#define CONFIG_SPI_CPHA        SPI_PHASE_2EDGE
#define CONFIG_SPI_DIVISOR     256
#define CONFIG_SPI_WORD_SIZE   SPI_DATASIZE_8BIT
#define CONFIG_SPI_WAIT_MS     1000

// Mapeamento dos pinos GPIO
#define QCA_SPI_CLK_PIN       GPIO_PIN_5
#define QCA_SPI_DATA_IN_PIN   GPIO_PIN_6
#define QCA_SPI_DATA_OUT_PIN  GPIO_PIN_7
#define QCA_SPI_CHIP_SEL_PIN  GPIO_PIN_4

Interface UART

// Parâmetros da comunicação UART
#define CONFIG_UART_SPEED        115200
#define CONFIG_UART_DATA_BITS    UART_WORDLENGTH_8D
#define CONFIG_UART_STOP_BITS    UART_STOPBITS_1
#define CONFIG_UART_PARITY_MODE  UART_PARITY_NO
#define CONFIG_UART_HW_FLOW      UART_HWCONTROL_NONE

// Pinos de controle de fluxo
#define QCA_UART_CTS_PIN         GPIO_PIN_0
#define QCA_UART_RTS_PIN         GPIO_PIN_1

Módulo de Driver SPI

#include "stm32f1xx_hal.h"

static SPI_HandleTypeDef spi_handle;

void InicializaSPI_QCA7000(void) {
    spi_handle.Instance               = SPI1;
    spi_handle.Init.Mode              = SPI_MODE_MASTER;
    spi_handle.Init.Direction         = SPI_DIRECTION_2LINES;
    spi_handle.Init.DataSize          = CONFIG_SPI_WORD_SIZE;
    spi_handle.Init.CLKPolarity       = CONFIG_SPI_CPOL;
    spi_handle.Init.CLKPhase          = CONFIG_SPI_CPHA;
    spi_handle.Init.NSS               = SPI_NSS_SOFT;
    spi_handle.Init.BaudRatePrescaler = CONFIG_SPI_DIVISOR;
    spi_handle.Init.FirstBit          = SPI_FIRSTBIT_MSB;
    HAL_SPI_Init(&spi_handle);
}

HAL_StatusTypeDef EnviaRecebeSPI_QCA7000(uint8_t *saida, uint8_t *entrada, uint16_t tamanho) {
    return HAL_SPI_TransmitReceive(&spi_handle, saida, entrada, tamanho, CONFIG_SPI_WAIT_MS);
}

uint8_t LeRegistrador_QCA7000(uint8_t endereco) {
    uint8_t pacote_envio[2] = {0x03, endereco};
    uint8_t pacote_recebe[2] = {0};

    HAL_GPIO_WritePin(QCA_SPI_CS_GPIO_Port, QCA_SPI_CHIP_SEL_PIN, GPIO_PIN_RESET);
    HAL_SPI_TransmitReceive(&spi_handle, pacote_envio, pacote_recebe, 2, CONFIG_SPI_WAIT_MS);
    HAL_GPIO_WritePin(QCA_SPI_CS_GPIO_Port, QCA_SPI_CHIP_SEL_PIN, GPIO_PIN_SET);

    return pacote_recebe[1];
}

HAL_StatusTypeDef EscreveRegistrador_QCA7000(uint8_t endereco, uint8_t valor) {
    uint8_t pacote_envio[2] = {0x02, endereco};
    return HAL_SPI_Transmit(&spi_handle, pacote_envio, 2, CONFIG_SPI_WAIT_MS);
}

Módullo de Driver UART

static UART_HandleTypeDef uart_handle;

void InicializaUART_QCA7000(void) {
    uart_handle.Instance          = USART2;
    uart_handle.Init.BaudRate     = CONFIG_UART_SPEED;
    uart_handle.Init.WordLength   = CONFIG_UART_DATA_BITS;
    uart_handle.Init.StopBits     = CONFIG_UART_STOP_BITS;
    uart_handle.Init.Parity       = CONFIG_UART_PARITY_MODE;
    uart_handle.Init.HwFlowCtl    = CONFIG_UART_HW_FLOW;
    HAL_UART_Init(&uart_handle);
}

HAL_StatusTypeDef TransmiteUART_QCA7000(uint8_t *dados, uint16_t tamanho) {
    return HAL_UART_Transmit(&uart_handle, dados, tamanho, CONFIG_SPI_WAIT_MS);
}

void USART2_IRQHandler(void) {
    HAL_UART_IRQHandler(&uart_handle);

    if (__HAL_UART_GET_FLAG(&uart_handle, UART_FLAG_RXNE)) {
        uint8_t byte_recebido;
        HAL_UART_Receive(&uart_handle, &byte_recebido, 1, 0);
        // Processamento do byte recebido
    }
}

Estrutura de Comandos SPI e Cálculo de CRC

typedef struct {
    uint8_t comando;
    uint8_t endereco;
    uint8_t payload[4];
    uint8_t checksum;
} ComandoQCA7000_t;

uint16_t CalculaCRC16(uint8_t *buffer, uint16_t tamanho) {
    uint16_t acumulador = 0xFFFF;

    for (uint16_t i = 0; i < tamanho; i++) {
        acumulador ^= (uint16_t)buffer[i] << 8;
        for (uint8_t bit = 0; bit < 8; bit++) {
            if (acumulador & 0x8000)
                acumulador = (acumulador << 1) ^ 0x1021;
            else
                acumulador <<= 1;
        }
    }
    return acumulador;
}

Fluxo de Troca de Dados entre MCU e QCA7000

sequenceDiagram
    participant Microcontrolador
    participant Chip_QCA7000
    Microcontrolador->>Chip_QCA7000: Quadro de comando (0x01 + endereço + dados)
    Chip_QCA7000->>Microcontrolador: Byte de status (0x00 = sucesso)
    Microcontrolador->>Chip_QCA7000: Solicitação de leitura (0x03 + endereço + CRC)
    Chip_QCA7000-->>Microcontrolador: Confirmação via RTS/CTS

Configuração de DMA para SPI

void ConfiguraDMA_SPI_QCA7000(void) {
    DMA_HandleTypeDef dma_tx_spi, dma_rx_spi;

    __HAL_RCC_DMA1_CLK_ENABLE();

    dma_tx_spi.Instance                 = DMA1_Channel2;
    dma_tx_spi.Init.Direction           = DMA_MEMORY_TO_PERIPH;
    dma_tx_spi.Init.PeriphInc           = DMA_PINC_DISABLE;
    dma_tx_spi.Init.MemInc              = DMA_MINC_ENABLE;
    dma_tx_spi.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    dma_tx_spi.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
    dma_tx_spi.Init.Mode                = DMA_NORMAL;
    HAL_DMA_Init(&dma_tx_spi);

    HAL_SPI_Transmit_DMA(&spi_handle, buffer_tx, len_dados);
}

Gerenciamento de Controle de Fluxo UART

void HabilitaFluxoUART_QCA7000(bool ativar) {
    if (ativar) {
        HAL_GPIO_WritePin(QCA_UART_CTS_GPIO_Port, QCA_UART_CTS_PIN, GPIO_PIN_SET);
        HAL_GPIO_WritePin(QCA_UART_RTS_GPIO_Port, QCA_UART_RTS_PIN, GPIO_PIN_RESET);
    } else {
        HAL_GPIO_WritePin(QCA_UART_CTS_GPIO_Port, QCA_UART_CTS_PIN, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(QCA_UART_RTS_GPIO_Port, QCA_UART_RTS_PIN, GPIO_PIN_SET);
    }
}

Captura com Analisador Lógico

Tempo | Sinal SPI           | Sinal UART
-----------------------------------------
0µs   | CLK:0 → MOSI:0x02
1µs   | CLK:1 → MOSI:0x03
2µs   | CLK:0 → MISO:0x01
3µs   | CLK:1 → MISO:0xA5

Script de Teste Automatizado

import serial
import spidev

conexao_spi = spidev.SpiDev()
conexao_spi.open(0, 0)
conexao_spi.max_speed_hz = 281250

conexao_uart = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)

def teste_escrita_leitura_spi():
    conexao_spi.xfer2([0x02, 0x10, 0x55])
    resultado = conexao_spi.xfer2([0x03, 0x10, 0x00])
    assert resultado[2] == 0x55

def teste_handshake_uart():
    conexao_uart.write(b'AT+STATUS\r\n')
    resposta = conexao_uart.read_until(b'\r\n')
    assert resposta == b'OK\r\n'

Otimização de Desempenho

Ajuste do clock SPI: Para cabos de até 1 metro, recomenda-se frequência máxima de 1 MHz. Ative a verificação CRC do SPI com polinômio 0x1021.

Buffer Circualr para Recepção UART

#define TAM_BUFFER_RX 256
volatile uint8_t buffer_rx_uart[TAM_BUFFER_RX];
volatile uint16_t posicao_rx = 0;

void USART2_IRQHandler(void) {
    if (__HAL_UART_GET_FLAG(&uart_handle, UART_FLAG_RXNE)) {
        uint8_t byte;
        HAL_UART_Receive(&uart_handle, &byte, 1, 0);
        buffer_rx_uart[posicao_rx++] = byte;
        if (posicao_rx >= TAM_BUFFER_RX)
            posicao_rx = 0;
    }
}

Modo de Baixo Consumo

void EntraSleepSPI_QCA7000(void) {
    HAL_SPI_Disable(&spi_handle);
    HAL_GPIO_WritePin(QCA_SPI_CS_GPIO_Port, QCA_SPI_CHIP_SEL_PIN, GPIO_PIN_SET);
}

void AcordaSPI_QCA7000(void) {
    HAL_GPIO_WritePin(QCA_SPI_CS_GPIO_Port, QCA_SPI_CHIP_SEL_PIN, GPIO_PIN_RESET);
    HAL_SPI_Enable(&spi_handle);
}

Configuração do Projeto no STM32CubeMX

  • Habiltiar periféricos SPI1 e USART2
  • Configurar prioridades NVIC (SPI acima de UART)
  • Ativar controlador DMA (Canal 2 para transferências SPI)

Makefile para Compilação

CC = arm-none-eabi-gcc
OPCOES_COMPILACAO = -mcpu=cortex-m3 -mthumb -O2 -Iinc -Isrc
OPCOES_LINK = -TSTM32F103C8Tx_FLASH.ld -specs=nosys.specs

alvo: firmware.elf

firmware.elf: main.o drv_spi.o drv_uart.o
	$(CC) $(OPCOES_COMPILACAO) -o $@ $^ $(OPCOES_LINK)

Solução de Problemas Comuns

Sintoma Diagnóstico Correção
Falha na comunicação SPI Frequência de clock anômala no analisador Revisar divisor de clock (BaudRatePrescaler)
Perda de dados UART Flag de overflow no buffer de recepção Implementar DMA com buffer duplo
Erro de verificação CRC Divergência entre valores calculados Validar implementação do polinômio 0x1021

Tags: QCA7000 STM32 SPI UART DMA

Publicado em 7-1 05:05