华大HC32F460轮询方式SPI通讯配置

背景

需要控制几块SPI通讯接口的屏幕显示内容。因此需要配置芯片SPI接口,软件方式此前已经轻松实现,但是出于性能考虑,使用硬件方式是更优的方案。

浏览了华大提供的代码例子,均是全双工场景下两个芯片相互发送接收数据调用的SPI_TransReceive函数,案例对于我需要的单方发送场景不太合适。

同时例子里发送的数据内容是固定的,在实际使用中还需重新修改。

本文会提供一种更简单易用的配置方式,方便单发送场景下的SPI硬件配置,同时不会涉及到屏幕控制的实际指令,仅包含对SPI接口相关进行配置,方便您快速移植代码

设计思路

1、简单的屏幕控制需求,无需配置过于复杂,简单易用即可,因此采用了阻塞式的轮询方案进行SPI通讯。

2、对于SPI通讯来说,最快速和占用资源最小的方式应该是采用DMA+中断的方式进行配置,但是这样配置和调用会比较麻烦,另一方面小型SPI屏幕本身性能有限,芯片发送指令速度再快,屏幕显示的速度还是没有什么差别的,因此无需配置过于复杂。

代码实现

1、SPI初始化

下面代码中需要注意:

1、无需接收数据因此SPI_MISO接口无需配置;

2、SPI_SS接口对应屏幕的片选CS,在主机模式下,460芯片似乎不会自动控制该引脚拉高和拉低,因此将该引脚作为普通的GPIO初始化,以进行手动的拉低和拉高;

3、stcSpiInit.u32SpiMode用于控制SPI工作模式,关系到MOSI和MISO的波形,该配置是导致SPI通讯成功与否的重要内容,460有0~3共4种工作模式。

c 复制代码
void InitSPI(void)
{	
	stc_spi_init_t stcSpiInit;//SPI CONFIG
	stc_irq_signin_config_t stcIrqSignConfig;//RX/TX
	stc_gpio_init_t stcGpioInit;//GPIO
		
		//GPIO初始化
    (void)GPIO_StructInit(&stcGpioInit);
    stcGpioInit.u16PinDrv       = PIN_HIGH_DRV;
    //(void)GPIO_Init(IPS_SPI_SS_PORT,   IPS_SPI_SS_PIN,   &stcGpioInit);
    (void)GPIO_Init(IPS_SPI_SCK_PORT,  IPS_SPI_SCK_PIN,  &stcGpioInit);
    (void)GPIO_Init(IPS_SPI_MOSI_PORT, IPS_SPI_MOSI_PIN, &stcGpioInit);
    //(void)GPIO_Init(SPI_MISO_PORT, SPI_MISO_PIN, &stcGpioInit);

    /* 配置引脚Func模式 */
    //GPIO_SetFunc(IPS_SPI_SS_PORT,   IPS_SPI_SS_PIN,   IPS_SPI_SS_FUNC);
    GPIO_SetFunc(IPS_SPI_SCK_PORT,  IPS_SPI_SCK_PIN,  IPS_SPI_SCK_FUNC);
    GPIO_SetFunc(IPS_SPI_MOSI_PORT, IPS_SPI_MOSI_PIN, IPS_SPI_MOSI_FUNC);
    //GPIO_SetFunc(SPI_MISO_PORT, SPI_MISO_PIN, SPI_MISO_FUNC);

    /* 配置 SPI */
	FCG_Fcg1PeriphClockCmd(IPS_SPI_CLK, ENABLE);
	SPI_StructInit(&stcSpiInit);
	stcSpiInit.u32WireMode          = SPI_4_WIRE;//四线SPI
	stcSpiInit.u32TransMode         = SPI_SEND_ONLY;//仅发送模式(也可配置全双工)
	stcSpiInit.u32MasterSlave       = IPS_EXAMPLE_SPI_MASTER_SLAVE;//主从机模式
	stcSpiInit.u32Parity            = SPI_PARITY_INVD;//奇偶校验模式,配置无校验
	stcSpiInit.u32SpiMode           = SPI_MD_3;//SPI工作模式,空闲状态低电平、上升沿采样,下降沿变化
	stcSpiInit.u32BaudRatePrescaler = SPI_BR_CLK_DIV256;//速率分频
	stcSpiInit.u32DataBits          = SPI_DATA_SIZE_8BIT;//数据位
	stcSpiInit.u32FirstBit          = SPI_FIRST_MSB;//最高有效位先传输,D7、D6...D0顺序
	stcSpiInit.u32FrameLevel        = SPI_1_FRAME;//向寄存器写入1位数据后立即推送至TX
	(void)SPI_Init(IPS_SPI_UNIT, &stcSpiInit);
	SPI_Cmd(IPS_SPI_UNIT,ENABLE);
		//SPI_IntCmd(SPI_UNIT, (SPI_INT_TX_BUF_EMPTY), ENABLE);//仅启用TX
    //SPI_IntCmd(SPI_UNIT, (SPI_INT_TX_BUF_EMPTY | SPI_INT_RX_BUF_FULL), ENABLE);

    /* TX NVIC configure */
//    stcIrqSignConfig.enIntSrc    = SPI_TX_IRQ_SRC;
//    stcIrqSignConfig.enIRQn      = SPI_TX_IRQ_NUM;
//    stcIrqSignConfig.pfnCallback = &SPI_TransCompleteCallback;
//    (void)INTC_IrqSignIn(&stcIrqSignConfig);
//    NVIC_ClearPendingIRQ(stcIrqSignConfig.enIRQn);
//    NVIC_SetPriority(stcIrqSignConfig.enIRQn, DDL_IRQ_PRIO_DEFAULT);
//    NVIC_EnableIRQ(stcIrqSignConfig.enIRQn);
    /* RX NVIC configure */
//    stcIrqSignConfig.enIntSrc    = SPI_RX_IRQ_SRC;
//    stcIrqSignConfig.enIRQn      = SPI_RX_IRQ_NUM;
//    stcIrqSignConfig.pfnCallback = &SPI_ReceiveCompleteCallback;
//    (void)INTC_IrqSignIn(&stcIrqSignConfig);
//    NVIC_ClearPendingIRQ(stcIrqSignConfig.enIRQn);
//    NVIC_SetPriority(stcIrqSignConfig.enIRQn, DDL_IRQ_PRIO_DEFAULT - 1);
//    NVIC_EnableIRQ(stcIrqSignConfig.enIRQn);
		
		//普通IO初始化
		(void)GPIO_StructInit(&stcGpioInit);
		stcGpioInit.u16PinState = PIN_STAT_RST;//默认置0
		stcGpioInit.u16PinDir = PIN_DIR_OUT;//输出模式

		(void)GPIO_Init(IPS_SPI_RES_PORT, IPS_SPI_RES_PIN, &stcGpioInit);
		(void)GPIO_Init(IPS_SPI_DC_PORT, IPS_SPI_DC_PIN, &stcGpioInit);
		(void)GPIO_Init(IPS_SPI_CS_PORT, IPS_SPI_CS_PIN, &stcGpioInit);

		stcGpioInit.u16PinState = PIN_STAT_SET;//背光默认置1常亮
		(void)GPIO_Init(IPS_SPI_BLK_PORT, IPS_SPI_BLK_PIN, &stcGpioInit);
		IPS_CS_HIGH;//片选默认拉高
}

2、初始化代码对应的头文件声明内容

c 复制代码
/* SPI definition */
#define IPS_EXAMPLE_SPI_MASTER_SLAVE        (SPI_MASTER)
#define IPS_SPI_UNIT                        (CM_SPI1)
#define IPS_SPI_CLK                         (FCG1_PERIPH_SPI1)
#define IPS_SPI_TX_IRQ_SRC                  (INT_SRC_SPI1_SPTI)
#define IPS_SPI_TX_IRQ_NUM                  (INT013_IRQn)
#define IPS_SPI_RX_IRQ_SRC                  (INT_SRC_SPI1_SPRI)
#define IPS_SPI_RX_IRQ_NUM                  (INT014_IRQn)

/*SPI通讯配置引脚,4个,屏幕可不配置MISO*/
///* SS(CS) */
//#define IPS_SPI_SS_PORT                     (GPIO_PORT_B)
//#define IPS_SPI_SS_PIN                      (GPIO_PIN_09)
//#define IPS_SPI_SS_FUNC                     (GPIO_FUNC_42)
/* CS */
#define IPS_SPI_CS_PORT                     (GPIO_PORT_B)
#define IPS_SPI_CS_PIN                      (GPIO_PIN_09)

/* SCK(CLK/SCL) */
#define IPS_SPI_SCK_PORT                    (GPIO_PORT_A)
#define IPS_SPI_SCK_PIN                     (GPIO_PIN_07)
#define IPS_SPI_SCK_FUNC                    (GPIO_FUNC_43)
/* MOSI(Master Out Slave In/SDA/主机输出从机输入) */
#define IPS_SPI_MOSI_PORT                   (GPIO_PORT_A)
#define IPS_SPI_MOSI_PIN                    (GPIO_PIN_06)
#define IPS_SPI_MOSI_FUNC                   (GPIO_FUNC_40)
/* MISO(Master In Slave Out/主机输入从机输出) = PC5 */
//#define IPS_SPI_MISO_PORT                   (GPIO_PORT_C)
//#define IPS_SPI_MISO_PIN                    (GPIO_PIN_05)
//#define IPS_SPI_MISO_FUNC                   (GPIO_FUNC_41)

/* 额外的功能性引脚 */
/* RES */
#define IPS_SPI_RES_PORT                     (GPIO_PORT_A)
#define IPS_SPI_RES_PIN                      (GPIO_PIN_05)
/* DC */
#define IPS_SPI_DC_PORT                     (GPIO_PORT_A)
#define IPS_SPI_DC_PIN                      (GPIO_PIN_04)

/* BLK */
#define IPS_SPI_BLK_PORT                     (GPIO_PORT_A)
#define IPS_SPI_BLK_PIN                      (GPIO_PIN_03)

#define IPS_CS_LOW  ( GPIO_ResetPins(IPS_SPI_CS_PORT, IPS_SPI_CS_PIN) )
#define IPS_CS_HIGH  ( GPIO_SetPins(IPS_SPI_CS_PORT, IPS_SPI_CS_PIN) )

/* TFT屏相关引脚高低电平控制 */
//额外的通用引脚
#define IPS_DC_LOW  ( GPIO_ResetPins(IPS_SPI_DC_PORT, IPS_SPI_DC_PIN) )
#define IPS_DC_HIGH  ( GPIO_SetPins(IPS_SPI_DC_PORT, IPS_SPI_DC_PIN) )
#define IPS_RES_LOW  ( GPIO_ResetPins(IPS_SPI_RES_PORT, IPS_SPI_RES_PIN) )
#define IPS_RES_HIGH  ( GPIO_SetPins(IPS_SPI_RES_PORT, IPS_SPI_RES_PIN) )
#define IPS_BLK_LOW  ( GPIO_ResetPins(IPS_SPI_BLK_PORT, IPS_SPI_BLK_PIN) )
#define IPS_BLK_HIGH  ( GPIO_SetPins(IPS_SPI_BLK_PORT, IPS_SPI_BLK_PIN) )

3、SPI数据发送接口

为确保发送数据的时序,发送前需要等待SPI_FLAG_TX_BUF_EMPTY状态,发送后需要等待SPI_FLAG_IDLE状态确保发送完成。

否则数据还未发送完成就拉高CS会导致硬件SPI还未发送出刚写入的数据就通知屏幕停止接收数据,导致数据帧截断。

经测试,SPI_FLAG_TX_BUF_EMPTY可以不等待,但是必须等待SPI_FLAG_IDLE状态,出于严谨考虑,两个状态都进行等待较好。

c 复制代码
void IPS_SPI_WriteData_M(u8 Data)
{
	//CS_LOW; /* 拉低片选 */
	while(RESET == SPI_GetStatus(IPS_SPI_UNIT,SPI_FLAG_TX_BUF_EMPTY));
	SPI_WriteData(IPS_SPI_UNIT, Data);
	/* 等待 SPI 完全发送完成(关键) */ 
	while (RESET == SPI_GetStatus(IPS_SPI_UNIT, SPI_FLAG_IDLE));
	//CS_HIGH; /* 拉高片选 */
}

如下为屏幕指令写入接口和数据写入接口,通过DC脚的状态不同来控制屏幕区分下面发送的是指令还是数据。

c 复制代码
/**
 * @brief  液晶屏写指令.
 * @param  Data[in] 指令数据
 * @retval None
 */
void TFT_WriteCmd(u8 Data)
{
	IPS_DC_LOW;
	IPS_CS_LOW; /* 拉低片选 */
	IPS_SPI_WriteData_M(Data);
	IPS_CS_HIGH; /* 拉高片选 */
	IPS_DC_HIGH;	
}
/**
 * @brief  液晶屏写数据.
 * @param  Data[in] 数据
 * @retval None
 */
void TFT_WriteData(u8 Data)
{	
	IPS_DC_HIGH;
	IPS_CS_LOW; /* 拉低片选 */
	IPS_SPI_WriteData_M(Data);
	IPS_CS_HIGH; /* 拉高片选 */
	IPS_DC_HIGH;
}

/**
 * @brief  液晶屏写数据.
 * @param  Data[in] 2字节数据
 * @retval None
 */
void TFT_WriteData_16(u32 Data)
{
	IPS_DC_HIGH;
	IPS_CS_LOW; /* 拉低片选 */
	IPS_SPI_WriteData_M(Data>>8);
	IPS_SPI_WriteData_M(Data&0xFF);
	IPS_CS_HIGH; /* 拉高片选 */
	IPS_DC_HIGH;
}
相关推荐
czhaii17 小时前
MP3音乐播放器【FatFs+SD/TF卡+I2S-DAC】@STC32G144K246,实时解码MP3
单片机·硬件工程
bai54593620 小时前
STM32 CubeIDE 使用串口中断模式
stm32·单片机·嵌入式硬件
qq_4017004121 小时前
基于TMI6263BH芯片USB2.0 3.0电源控制与限流保护原理图
单片机·嵌入式硬件
fanged1 天前
STM32(4)--时钟树
stm32·单片机·嵌入式硬件
List<String> error_P1 天前
STM32启动流程及相关概念
单片机·嵌入式硬件
__万波__1 天前
STM32L475蜂鸣器实验
stm32·单片机·嵌入式硬件
qq_401700411 天前
经典78M05 LDO经典应用电路
单片机·嵌入式硬件
欢乐熊嵌入式编程1 天前
BLE 蓝牙开发入门:用手机控制开发板 LED
单片机·ble蓝牙开发·嵌入式入门
小美单片机1 天前
External model DLL ”ADC083XDLL“ not found_proteus仿真报错解决方法
c语言·单片机·51单片机·proteus·课程设计·课设