Files
TCP2UART/Drivers/CH390/CH390_Interface.c
T

355 lines
10 KiB
C

/********************************** (C) COPYRIGHT *****************************
* File Name : CH390_Interface.c
* Author : WCH (Modified for STM32 HAL)
* Version : V1.1
* Date : 2024/08/20
* Description : CH390 interface for STM32 HAL Library (SPI mode)
******************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*
* Modified for STM32F103 HAL Library with FreeRTOS support.
******************************************************************************/
#include "stm32f1xx_hal.h"
#include "main.h"
#include "CH390.h"
#include "CH390_Interface.h"
/* FreeRTOS includes */
#ifdef USE_FREERTOS
#include "FreeRTOS.h"
#include "task.h"
#endif
/*
* This file defines CH390 operation interface using STM32 HAL Library.
* Only SPI mode is implemented for this project.
*
* Hardware connections:
* - PA4: SPI1_NSS (directly controlled as GPIO for CS)
* - PA5: SPI1_SCK
* - PA6: SPI1_MISO
* - PA7: SPI1_MOSI
* - PB0: CH390 INT (EXTI0)
* - PB1: CH390 RST
*/
#ifdef CH390_INTERFACE_SPI
/* GPIO Pin Definitions - matching CubeMX configuration */
#define CH390_CS_PORT GPIOA
#define CH390_CS_PIN GPIO_PIN_4
#define CH390_RST_PORT GPIOB
#define CH390_RST_PIN GPIO_PIN_1
#define CH390_INT_PORT GPIOB
#define CH390_INT_PIN GPIO_PIN_0
/* External SPI handle from spi.c */
extern SPI_HandleTypeDef hspi1;
/* Timeout for SPI operations (ms) */
#define SPI_TIMEOUT 100
/*----------------------------------------------------------------------------
* Low-level GPIO operations
*---------------------------------------------------------------------------*/
/**
* @brief Set CS pin state
* @param state 0=low (select), 1=high (deselect)
*/
static inline void ch390_cs(uint8_t state)
{
HAL_GPIO_WritePin(CH390_CS_PORT, CH390_CS_PIN,
state ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/**
* @brief Set RST pin state
* @param state 0=low (reset), 1=high (normal)
*/
static inline void ch390_rst(uint8_t state)
{
HAL_GPIO_WritePin(CH390_RST_PORT, CH390_RST_PIN,
state ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/*----------------------------------------------------------------------------
* SPI Communication
*---------------------------------------------------------------------------*/
/**
* @brief Exchange one byte over SPI
* @param byte Byte to send
* @return Received byte
*/
static uint8_t ch390_spi_exchange_byte(uint8_t byte)
{
uint8_t rx_data = 0;
HAL_SPI_TransmitReceive(&hspi1, &byte, &rx_data, 1, SPI_TIMEOUT);
return rx_data;
}
/**
* @brief Read a dummy byte (send 0x00)
* @return Received byte
*/
#define ch390_spi_dummy_read() ch390_spi_exchange_byte(0x00)
/*----------------------------------------------------------------------------
* Public Interface Functions
*---------------------------------------------------------------------------*/
/**
* @brief Initialize CH390 GPIO pins
* @note CS and RST pins are configured here. SPI pins are handled by CubeMX.
* INT pin (PB0) is configured as EXTI in CubeMX.
*/
void ch390_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Enable GPIO clocks */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* Configure CS pin (PA4) as GPIO output - we control it manually */
/* Note: CubeMX may configure PA4 as SPI1_NSS, we need to reconfigure it */
HAL_GPIO_WritePin(CH390_CS_PORT, CH390_CS_PIN, GPIO_PIN_SET); /* Deselect */
GPIO_InitStruct.Pin = CH390_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(CH390_CS_PORT, &GPIO_InitStruct);
/* Configure RST pin (PB1) as output */
HAL_GPIO_WritePin(CH390_RST_PORT, CH390_RST_PIN, GPIO_PIN_SET); /* Not reset */
GPIO_InitStruct.Pin = CH390_RST_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(CH390_RST_PORT, &GPIO_InitStruct);
/* INT pin (PB0) is configured as EXTI input by CubeMX */
}
/**
* @brief Initialize CH390 interrupt (EXTI0 on PB0)
* @note EXTI and NVIC are configured in CubeMX. This function can enable/disable.
*/
void ch390_interrupt_init(void)
{
/* EXTI0 is configured in CubeMX for PB0 */
/* NVIC priority should be >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY */
/* for FreeRTOS compatibility */
HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/**
* @brief Initialize SPI for CH390
* @note SPI1 is already initialized by CubeMX. This function reconfigures
* for CH390 requirements (Mode 3: CPOL=High, CPHA=2Edge).
*/
void ch390_spi_init(void)
{
/* SPI1 is initialized by MX_SPI1_Init() in main.c */
/* We need to ensure correct SPI mode for CH390: */
/* - CPOL = High (idle clock is high) */
/* - CPHA = 2Edge (data captured on second edge) */
/* Reconfigure SPI for CH390 if needed */
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT; /* We control CS manually */
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
/* Handle error */
Error_Handler();
}
}
/**
* @brief Get CH390 interrupt pin state
* @return Non-zero if INT pin is high (active low interrupt)
*/
uint16_t ch390_get_int_pin(void)
{
return HAL_GPIO_ReadPin(CH390_INT_PORT, CH390_INT_PIN);
}
/**
* @brief Delay in microseconds
* @param time Delay time in microseconds
* @note Uses DWT cycle counter for accurate timing if available,
* otherwise falls back to simple loop delay.
*/
void ch390_delay_us(uint32_t time)
{
#ifdef USE_FREERTOS
/* For FreeRTOS, if delay is long enough, use vTaskDelay */
if (time >= 1000)
{
/* Convert to milliseconds and use FreeRTOS delay if in task context */
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)
{
vTaskDelay(pdMS_TO_TICKS(time / 1000));
time = time % 1000;
}
}
#endif
/* Short delay using DWT or simple loop */
if (time > 0)
{
/* Simple delay loop - approximately calibrated for 72MHz */
/* Each iteration is roughly 1/9 us at 72MHz */
volatile uint32_t count = time * 9;
while (count--)
{
__NOP();
}
}
}
/**
* @brief Hardware reset CH390 by pulling RST pin low
*/
void ch390_hardware_reset(void)
{
ch390_rst(0); /* Assert reset (low) */
ch390_delay_us(100); /* Hold reset for 100us (min 10us required) */
ch390_rst(1); /* Release reset (high) */
ch390_delay_us(10000); /* Wait 10ms for CH390 to initialize */
}
/*----------------------------------------------------------------------------
* CH390 Register/Memory Access Functions (SPI Mode)
*---------------------------------------------------------------------------*/
/**
* @brief Read a CH390 register
* @param reg Register address
* @return Register value
*/
uint8_t ch390_read_reg(uint8_t reg)
{
uint8_t value;
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(reg | OPC_REG_R); /* Send read command */
value = ch390_spi_dummy_read(); /* Read register value */
ch390_cs(1); /* CS high - deselect */
return value;
}
/**
* @brief Write a CH390 register
* @param reg Register address
* @param value Value to write
*/
void ch390_write_reg(uint8_t reg, uint8_t value)
{
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(reg | OPC_REG_W); /* Send write command */
ch390_spi_exchange_byte(value); /* Write register value */
ch390_cs(1); /* CS high - deselect */
}
/**
* @brief Read data from CH390 RX SRAM
* @param data Buffer to store received data
* @param length Number of bytes to read
*/
void ch390_read_mem(uint8_t *data, int length)
{
int i;
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(OPC_MEM_READ); /* Send memory read command */
/* Read data bytes */
for (i = 0; i < length; i++)
{
data[i] = ch390_spi_dummy_read();
}
ch390_cs(1); /* CS high - deselect */
}
/**
* @brief Read data from CH390 RX SRAM using DMA (for larger transfers)
* @param data Buffer to store received data
* @param length Number of bytes to read
* @note Falls back to polling mode for small transfers
*/
void ch390_read_mem_dma(uint8_t *data, int length)
{
/* For small transfers, use polling mode */
if (length < 64)
{
ch390_read_mem(data, length);
return;
}
/* For larger transfers, could use DMA - currently using polling */
/* TODO: Implement DMA transfer if needed for performance */
ch390_read_mem(data, length);
}
/**
* @brief Write data to CH390 TX SRAM
* @param data Data buffer to send
* @param length Number of bytes to write
*/
void ch390_write_mem(uint8_t *data, int length)
{
int i;
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(OPC_MEM_WRITE); /* Send memory write command */
/* Write data bytes */
for (i = 0; i < length; i++)
{
ch390_spi_exchange_byte(data[i]);
}
ch390_cs(1); /* CS high - deselect */
}
/**
* @brief Write data to CH390 TX SRAM using DMA (for larger transfers)
* @param data Data buffer to send
* @param length Number of bytes to write
* @note Falls back to polling mode for small transfers
*/
void ch390_write_mem_dma(uint8_t *data, int length)
{
/* For small transfers, use polling mode */
if (length < 64)
{
ch390_write_mem(data, length);
return;
}
/* For larger transfers, could use DMA - currently using polling */
/* TODO: Implement DMA transfer if needed for performance */
ch390_write_mem(data, length);
}
#else /* CH390_INTERFACE_SPI */
/*
* Non-SPI modes (8-bit/16-bit parallel) are not implemented for this project.
* This project uses SPI interface only.
*/
#error "This project only supports CH390 SPI interface. Please define CH390_INTERFACE_SPI in CH390.h"
#endif /* CH390_INTERFACE_SPI */