【联盛德W806上手笔记】八、SPI及其DMA

2023-05-16

目录

  • 高速 SPI 设备控制器
  • 库函数
    • 函数
    • 参数
    • 应用示例
      • 初始化
      • 引脚复用
      • 中断相关
      • 使用
  • 注意事项
    • DataAlignment
    • Direction
  • 测试
    • main.c
    • wm_hal_msp.c
    • wm_it.c
    • 其他改动
    • 实验现象

Windows 10 20H2
HLK-W806-V1.0-KIT
WM_SDK_W806_v0.6.0


       摘自《W806 芯片设计指导书 V1.0》、《W806 MCU 芯片规格书 V2.0》、《WM_W800_寄存器手册 V2.1》

高速 SPI 设备控制器

       兼容通用 SPI 物理层协议,通过约定与主机交互的数据格式,主机对设备的高速访问,最高支持工作频率为50Mbps。

兼容通用 SPI 协议
可选择的电平中断信号
最高支持 50Mbps 速率
简单的帧格式,全硬件解析与 DMA

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

库函数

函数

HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
//初始化SPI的工作模式和速率等

HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi);
//将初始化之后的SPI接口恢复成默认的状态–各个寄存器复位时的值

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi);
//用于启用SPI时钟,引脚复用,配置DMA及中断优先级等

void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi);
//用于禁用SPI时钟,将对应引脚恢复成默认的状态

HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size, uint32_t Timeout);
//SPI发送数据,使用超时管理机制 
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size, uint32_t Timeout);
//SPI接收数据,使用超时管理机制 
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint32_t Size,
                                          uint32_t Timeout);
//SPI收发数据,使用超时管理机制 

HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size);
//SPI中断模式发送
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size);
//SPI中断模式接收
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
                                             uint32_t Size);
//SPI中断模式收发

HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size);
//SPI DMA模式发送
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size);
//SPI DMA模式接收
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
                                              uint32_t Size);
//SPI DMA模式收发
											  
HAL_StatusTypeDef HAL_SPI_DMAPause(SPI_HandleTypeDef *hspi);
//SPI DMA传输暂停
HAL_StatusTypeDef HAL_SPI_DMAResume(SPI_HandleTypeDef *hspi);
//SPI DMA传输恢复
HAL_StatusTypeDef HAL_SPI_DMAStop(SPI_HandleTypeDef *hspi);
//SPI DMA传输停止

void HAL_SPI_IRQHandler(SPI_HandleTypeDef *hspi);
//SPI 中断服务函数
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);
//SPI 发送完毕回调函数
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi);
//SPI 接收完毕回调函数
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi);
//SPI 收发完毕回调函数
void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxRxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);

HAL_SPI_StateTypeDef HAL_SPI_GetState(SPI_HandleTypeDef *hspi);
//获取SPI传输状态
uint32_t             HAL_SPI_GetError(SPI_HandleTypeDef *hspi);

参数

结构体和枚举类型

typedef struct
{
	uint32_t Mode;                /*!< Specifies the SPI operating mode.
									 This parameter can be a value of @ref SPI_Mode */

	uint32_t CLKPolarity;         /*!< Specifies the serial clock steady state.
									 This parameter can be a value of @ref SPI_Clock_Polarity */

	uint32_t CLKPhase;            /*!< Specifies the clock active edge for the bit capture.
									 This parameter can be a value of @ref SPI_Clock_Phase */

	uint32_t NSS;                 /*!< Specifies whether the NSS signal is managed by
									 hardware (NSS pin) or by software using the SSI bit.
									 This parameter can be a value of @ref SPI_Slave_Select_management */

	uint32_t BaudRatePrescaler;   /*!< Specifies the Baud Rate prescaler value which will be
									 used to configure the transmit and receive SCK clock.
									 This parameter can be a value of @ref SPI_BaudRate_Prescaler
									 @note The communication clock is derived from the master
									 clock. The slave clock does not need to be set. */
									 
	uint32_t FirstByte;			  /* Specifies whether data transfers start from high byte or low byte.
									 This parameter can be a value of @ref SPI_HBYTE_LBYTE_transmission */
									 

} SPI_InitTypeDef;

typedef enum
{
	HAL_SPI_STATE_RESET      = 0x00U,    /*!< Peripheral not Initialized                         */
	HAL_SPI_STATE_READY      = 0x01U,    /*!< Peripheral Initialized and ready for use           */
	HAL_SPI_STATE_BUSY       = 0x02U,    /*!< an internal process is ongoing                     */
	HAL_SPI_STATE_BUSY_TX    = 0x03U,    /*!< Data Transmission process is ongoing               */
	HAL_SPI_STATE_BUSY_RX    = 0x04U,    /*!< Data Reception process is ongoing                  */
	HAL_SPI_STATE_BUSY_TX_RX = 0x05U,    /*!< Data Transmission and Reception process is ongoing */
	HAL_SPI_STATE_ERROR      = 0x06U,    /*!< SPI error state                                    */
	HAL_SPI_STATE_ABORT      = 0x07U     /*!< SPI abort is ongoing                               */
} HAL_SPI_StateTypeDef;

typedef struct __SPI_HandleTypeDef
{
	SPI_TypeDef                *Instance;      /*!< SPI registers base address               */

	SPI_InitTypeDef            Init;           /*!< SPI communication parameters             */

	uint8_t                    *pTxBuffPtr;    /*!< Pointer to SPI Tx transfer Buffer        */

	uint32_t                   TxXferSize;     /*!< SPI Tx Transfer size                     */

	__IO uint32_t              TxXferCount;    /*!< SPI Tx Transfer Counter                  */

	uint8_t                    *pRxBuffPtr;    /*!< Pointer to SPI Rx transfer Buffer        */

	uint32_t                   RxXferSize;     /*!< SPI Rx Transfer size                     */

	__IO uint32_t              RxXferCount;    /*!< SPI Rx Transfer Counter                  */
	
	DMA_HandleTypeDef		   *hdmatx;		   /*!< SPI Tx DMA Handle parameters			 */
	
	DMA_HandleTypeDef		   *hdmarx;		   /*!< SPI Rx DMA Handle parameters			 */

	HAL_LockTypeDef            Lock;           /*!< Locking object                           */

	__IO HAL_SPI_StateTypeDef  State;          /*!< SPI communication state                  */

	__IO uint32_t              ErrorCode;      /*!< SPI Error code                           */

} SPI_HandleTypeDef;

宏参数

#define SPI                ((SPI_TypeDef *)SPI_BASE)

#define HAL_SPI_ERROR_NONE              (0x00000000U)   /*!< No error                               */
#define HAL_SPI_ERROR_TXERR				(0x00000001U)	/*!< Tx error                               */
#define HAL_SPI_ERROR_RXERR				(0x00000002U)	/*!< Rx error                               */
#define HAL_SPI_ERROR_DMA               (0x00000010U)   /*!< DMA transfer error                     */


// SPI_Mode
#define SPI_MODE_SLAVE                  (0x00000000U)
#define SPI_MODE_MASTER                 (SPI_SPI_CFG_MASTER)

// SPI_Clock_Polarity
#define SPI_POLARITY_LOW                (0x00000000U)
#define SPI_POLARITY_HIGH               SPI_SPI_CFG_CPOL

// SPI_Clock_Phase
#define SPI_PHASE_1EDGE                 (0x00000000U)
#define SPI_PHASE_2EDGE                 SPI_SPI_CFG_CPHA

// SPI_Slave_Select_management
#define SPI_NSS_HARD             		(0x00000000U)
#define SPI_NSS_SOFT                    SPI_CH_CFG_CSSEL

// SPI_HBYTE_LBYTE_transmission
#define SPI_LITTLEENDIAN				(0x00000000U)
#define SPI_BIGENDIAN					SPI_SPI_CFG_BIGENDIAN

// fclk = 40MHz / (2 * (div + 1))
#define SPI_BAUDRATEPRESCALER_2         (0x00000000U)	// 40M / 2 = 20M
#define SPI_BAUDRATEPRESCALER_4         (0x00000001U)	// 40M / 4 = 10M
#define SPI_BAUDRATEPRESCALER_8         (0x00000003U)	// 40M / 8 = 5M
#define SPI_BAUDRATEPRESCALER_10        (0x00000004U)	// 40M / 10 = 4M
#define SPI_BAUDRATEPRESCALER_20        (0x00000009U)	// 40M / 20 = 2M
#define SPI_BAUDRATEPRESCALER_40        (0x00000013U)	// 40M / 40 = 1M


#define BLOCK_SIZE (8 * 1024 - 8)

#define IS_SPI_ALL_INSTANCE(INSTANCE) ((INSTANCE) == SPI1)

#define IS_SPI_MODE(__MODE__)      ((__MODE__) == SPI_MODE_MASTER)

#define IS_SPI_NSS(__NSS__)        (((__NSS__) == SPI_NSS_SOFT)       || \
                                    ((__NSS__) == SPI_NSS_HARD_INPUT) || \
                                    ((__NSS__) == SPI_NSS_HARD_OUTPUT))
									
#define IS_SPI_BIG_OR_LITTLE(__ENDIAN__)	   (((__ENDIAN__) == SPI_LITTLEENDIAN) || \
												((__ENDIAN__) == SPI_BIGENDIAN))

#define IS_SPI_DMA_HANDLE(__HANDLE__)	((__HANDLE__) != NULL)
									
#define __HAL_SPI_ENABLE_TX(__HANDLE__)  SET_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_TXON)

#define __HAL_SPI_DISABLE_TX(__HANDLE__)	CLEAR_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_TXON)

#define __HAL_SPI_ENABLE_RX(__HANDLE__)  SET_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_RXON)

#define __HAL_SPI_DISABLE_RX(__HANDLE__)  CLEAR_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_RXON)

#define __HAL_SPI_ENABLE_TXRX(__HANDLE__)  SET_BIT((__HANDLE__)->Instance->CH_CFG, (SPI_CH_CFG_RXON | SPI_CH_CFG_TXON))

#define __HAL_SPI_DISABLE_TXRX(__HANDLE__) CLEAR_BIT((__HANDLE__)->Instance->CH_CFG, (SPI_CH_CFG_RXON | SPI_CH_CFG_TXON))

#define __HAL_SPI_CLEAR_FIFO(__HANDLE__) do{SET_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_CLEARFIFOS);\
											while(READ_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_CLEARFIFOS));}while(0U);

#define __HAL_SPI_GET_TXFIFO(__HANDLE__) (((__HANDLE__)->Instance->STATUS) & SPI_STATUS_TXFIFO)

#define __HAL_SPI_GET_RXFIFO(__HANDLE__) ((((__HANDLE__)->Instance->STATUS) & SPI_STATUS_RXFIFO) >> SPI_STATUS_RXFIFO_Pos)

#define __HAL_SPI_SET_CLK_NUM(__HANDLE__, NUM)	(MODIFY_REG((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_LEN, NUM << SPI_CH_CFG_LEN_Pos))

#define __HAL_SPI_SET_START(__HANDLE__) SET_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_START)

#define __HAL_SPI_GET_BUSY_STATUS(__HANDLE__)	(READ_BIT((__HANDLE__)->Instance->STATUS, SPI_STATUS_BUSY))

#define __HAL_SPI_SET_CS_LOW(__HANDLE__)	CLEAR_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_CSLEVEL)

#define __HAL_SPI_SET_CS_HIGH(__HANDLE__)	SET_BIT((__HANDLE__)->Instance->CH_CFG, SPI_CH_CFG_CSLEVEL)

#define __HAL_SPI_GET_FLAG(__HANDLE__, FLAG)	READ_BIT((__HANDLE__)->Instance->INT_SRC, FLAG)

#define __HAL_SPI_CLEAR_FLAG(__HANDLE__, FLAG)	SET_BIT((__HANDLE__)->Instance->INT_SRC, FLAG)

#define __HAL_SPI_ENABLE_IT(__HANDLE__, IT)		CLEAR_BIT((__HANDLE__)->Instance->INT_MASK, IT)

#define __HAL_SPI_DISABLE_IT(__HANDLE__, IT)	SET_BIT((__HANDLE__)->Instance->INT_MASK, IT)

应用示例

       摘自spi例程

初始化

在main.c中

SPI_HandleTypeDef hspi;
DMA_HandleTypeDef hdma_spi_tx;
DMA_HandleTypeDef hdma_spi_rx;

static void SPI_Init(void);
static void DMA_Init(void);

static void SPI_Init(void)
{
	hspi.Instance = SPI;
	hspi.Init.Mode = SPI_MODE_MASTER;						//工作模式
	hspi.Init.CLKPolarity = SPI_POLARITY_LOW;				//控制空闲时的时钟电平
	hspi.Init.CLKPhase = SPI_PHASE_1EDGE;					//控制采样相位
	hspi.Init.NSS = SPI_NSS_SOFT;							//片选引脚的控制方式
	hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_20;	//分频系数 其中SPI时钟为40MHz
	hspi.Init.FirstByte = SPI_LITTLEENDIAN;					//控制数据 MSB或LSB
	
	if (HAL_SPI_Init(&hspi) != HAL_OK)
	{
		Error_Handler();
	}
}

static void DMA_Init(void)
{
	__HAL_RCC_DMA_CLK_ENABLE();
	
	HAL_NVIC_SetPriority(DMA_Channel0_IRQn, 0);
	HAL_NVIC_EnableIRQ(DMA_Channel0_IRQn);
	
	HAL_NVIC_SetPriority(DMA_Channel1_IRQn, 0);
	HAL_NVIC_EnableIRQ(DMA_Channel1_IRQn);
}

引脚复用

在wm_hal_msp.c中

extern DMA_HandleTypeDef hdma_spi_tx;
extern DMA_HandleTypeDef hdma_spi_rx;

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
	__HAL_RCC_SPI_CLK_ENABLE();						//启用SPI时钟
	__HAL_AFIO_REMAP_SPI_CS(GPIOB, GPIO_PIN_4);		//SPI引脚复用
	__HAL_AFIO_REMAP_SPI_CLK(GPIOB, GPIO_PIN_2);
	__HAL_AFIO_REMAP_SPI_MISO(GPIOB, GPIO_PIN_3);
	__HAL_AFIO_REMAP_SPI_MOSI(GPIOB, GPIO_PIN_5);	
	
	hdma_spi_tx.Instance = DMA_Channel0;							//使用的DMA通道
	hdma_spi_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;				//DMA传输方向 这里疑似有问题,见后文
	hdma_spi_tx.Init.DestInc = DMA_DINC_DISABLE;					//目标地址是否自增
	hdma_spi_tx.Init.SrcInc = DMA_SINC_ENABLE;						//数据地址是否自增
	hdma_spi_tx.Init.DataAlignment = DMA_DATAALIGN_WORD;			//数据对齐方式 这里疑似有问题,见后文
	hdma_spi_tx.Init.Mode = DMA_MODE_NORMAL_SINGLE;					//DMA工作模式
	hdma_spi_tx.Init.RequestSourceSel = DMA_REQUEST_SOURCE_SPI_TX;	//DMA请求源
	
	__HAL_LINKDMA(hspi, hdmatx, hdma_spi_tx);
	if (HAL_DMA_Init(&hdma_spi_tx) != HAL_OK)
	{
		Error_Handler();
	}
	
	hdma_spi_rx.Instance = DMA_Channel1;
	hdma_spi_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
	hdma_spi_rx.Init.DestInc = DMA_DINC_ENABLE;
	hdma_spi_rx.Init.SrcInc = DMA_SINC_DISABLE;
	hdma_spi_rx.Init.DataAlignment = DMA_DATAALIGN_WORD;
	hdma_spi_rx.Init.Mode = DMA_MODE_NORMAL_SINGLE;
	hdma_spi_rx.Init.RequestSourceSel = DMA_REQUEST_SOURCE_SPI_RX;
	
	__HAL_LINKDMA(hspi, hdmarx, hdma_spi_rx);
	if (HAL_DMA_Init(&hdma_spi_rx) != HAL_OK)
	{
		Error_Handler();
	}
	
	HAL_NVIC_SetPriority(SPI_LS_IRQn, 1);
	HAL_NVIC_EnableIRQ(SPI_LS_IRQn);
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{
	__HAL_RCC_SPI_CLK_DISABLE();
	HAL_GPIO_DeInit(GPIOB, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);
}

中断相关

在wm_it.c中

extern SPI_HandleTypeDef hspi;
extern DMA_HandleTypeDef hdma_spi_tx;
extern DMA_HandleTypeDef hdma_spi_rx;

__attribute__((isr)) void SPI_LS_IRQHandler(void)
{
	HAL_SPI_IRQHandler(&hspi);
}

__attribute__((isr)) void DMA_Channel0_IRQHandler(void)
{
	HAL_DMA_IRQHandler(&hdma_spi_tx);
}

__attribute__((isr)) void DMA_Channel1_IRQHandler(void)
{
	HAL_DMA_IRQHandler(&hdma_spi_rx);
}

使用

	DMA_Init();
	SPI_Init();

	__HAL_SPI_SET_CS_LOW(&hspi);
	HAL_SPI_Transmit_DMA(&hspi, 数据首地址, 数据长度);

	__HAL_SPI_SET_CS_LOW(&hspi);
	HAL_SPI_Receive_DMA(&hspi, 缓冲区首地址, 数据长度);

注意事项

DataAlignment

       在WM_SDK_W806_v0.6.0中,DMA的数据对齐方式DataAlignment和STM32的效果完全相反,不知道是否是有意为之:
       所传数据为
在这里插入图片描述
当hdma_spi_tx.Init.DataAlignment = DMA_DATAALIGN_WORD时
在这里插入图片描述
波形正确
在这里插入图片描述
使用硬件I2C+DMA驱动相同的屏幕,在STM32中则为
在这里插入图片描述
若改为hdma_spi_tx.Init.DataAlignment = DMA_DATAALIGN_BYTE;
在这里插入图片描述
则波形错误
在这里插入图片描述

Direction

在WM_SDK_W806_v0.6.0中,外设到内存的宏和内存到外设的宏是一样的,有点奇怪
在这里插入图片描述
在STM32中则是不一样的:
在这里插入图片描述

测试

这里的测试程序见【0.96寸 OLED屏实现1500Fps的帧率】STM32 软件、硬件SPI、I2C驱动总结

main.c

#include <stdio.h>
#include "wm_hal.h"
#include "oled.h"

SPI_HandleTypeDef hspi;
DMA_HandleTypeDef hdma_spi_tx;

void Error_Handler(void);
static void SPI_Init(void);
static void GPIO_Init(void);
static void DMA_Init(void);

uint16_t FPS = 0, FPS_Count = 0;

int main(void)
{
	SystemClock_Config(CPU_CLK_240M);
	GPIO_Init();
	DMA_Init();
	SPI_Init();
	
	printf("enter main\r\n");

	uint8_t i;

	OLED_Init();
	OLED_Clear();
	OLED_Display_On();	     
	OLED_ShowString(0, 0, "   W806  OLED", 16, 0);
	OLED_ShowString(0, 2, "   2022-01-25", 6, 0);

	for(i = 0; i < 7; ++i)
		OLED_ShowChinese(8 + 16 * i, 6, i, 1);

	OLED_Refresh_Gram();

	while (1)
	{
		++FPS_Count;
		OLED_ShowString(0, 4, "FPS: ", 16, 0); 
		OLED_ShowNum(32, 4, FPS, 7, 16, 0);
		OLED_Refresh_Gram();
	}
}

static void SPI_Init(void)
{
	hspi.Instance = SPI;
	hspi.Init.Mode = SPI_MODE_MASTER;
	hspi.Init.CLKPolarity = SPI_POLARITY_HIGH;
	hspi.Init.CLKPhase = SPI_PHASE_2EDGE;
	hspi.Init.NSS = SPI_NSS_SOFT;
	hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
	hspi.Init.FirstByte = SPI_LITTLEENDIAN;

	if (HAL_SPI_Init(&hspi) != HAL_OK)
	{
		Error_Handler();
	}
}

static void GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	__HAL_RCC_GPIO_CLK_ENABLE();
	GPIO_InitStruct.Pin = SSD1306_RES_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(SSD1306_RES_PORT, &GPIO_InitStruct);
	GPIO_InitStruct.Pin = SSD1306_DC_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(SSD1306_DC_PORT, &GPIO_InitStruct);
}

static void DMA_Init(void)
{
	__HAL_RCC_DMA_CLK_ENABLE();
	
	HAL_NVIC_SetPriority(DMA_Channel0_IRQn, 0);
	HAL_NVIC_EnableIRQ(DMA_Channel0_IRQn);
}

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{

}

void Error_Handler(void)
{
	while (1)
	{
	}
}

void assert_failed(uint8_t *file, uint32_t line)
{
	printf("Wrong parameters value: file %s on line %d\r\n", file, line);
}

wm_hal_msp.c

#include "wm_hal.h"
#include "oled.h"

extern DMA_HandleTypeDef hdma_spi_tx;

void Error_Handler(void);

void HAL_MspInit(void)
{

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
    __HAL_RCC_SPI_CLK_ENABLE();
    __HAL_AFIO_REMAP_SPI_CS(SSD1306_CS_PORT, SSD1306_CS_PIN);
    __HAL_AFIO_REMAP_SPI_CLK(SSD1306_SCK_PORT, SSD1306_SCK_PIN);
    __HAL_AFIO_REMAP_SPI_MOSI(SSD1306_MOSI_PORT, SSD1306_MOSI_PIN);
	
	hdma_spi_tx.Instance = DMA_Channel0;
	hdma_spi_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
	hdma_spi_tx.Init.DestInc = DMA_DINC_DISABLE;
	hdma_spi_tx.Init.SrcInc = DMA_SINC_ENABLE;
	
//	hdma_spi_tx.Init.DataAlignment = DMA_DATAALIGN_BYTE;
	hdma_spi_tx.Init.DataAlignment = DMA_DATAALIGN_WORD;
	
	hdma_spi_tx.Init.Mode = DMA_MODE_NORMAL_SINGLE;
	hdma_spi_tx.Init.RequestSourceSel = DMA_REQUEST_SOURCE_SPI_TX;
	
	__HAL_LINKDMA(hspi, hdmatx, hdma_spi_tx);
	if (HAL_DMA_Init(&hdma_spi_tx) != HAL_OK)
	{
		Error_Handler();
	}
	
	HAL_NVIC_SetPriority(SPI_LS_IRQn, 1);
	HAL_NVIC_EnableIRQ(SPI_LS_IRQn);
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{
    __HAL_RCC_SPI_CLK_DISABLE();
    HAL_GPIO_DeInit(SSD1306_CS_PORT, SSD1306_CS_PIN);
    HAL_GPIO_DeInit(SSD1306_SCK_PORT, SSD1306_SCK_PIN);
    HAL_GPIO_DeInit(SSD1306_MOSI_PORT, SSD1306_MOSI_PIN);
}

wm_it.c


#include "wm_hal.h"

extern uint16_t FPS, FPS_Count;

extern SPI_HandleTypeDef hspi;
extern DMA_HandleTypeDef hdma_spi_tx;

#define readl(addr) ({unsigned int __v = (*(volatile unsigned int *) (addr)); __v;})
__attribute__((isr)) void CORET_IRQHandler(void)
{
	static uint16_t ms_Count = 0;
	readl(0xE000E010);
	HAL_IncTick();
	if(++ms_Count >= 1000)
	{
		ms_Count = 0;
		FPS = FPS_Count;
		FPS_Count = 0;
	}
}

__attribute__((isr)) void SPI_LS_IRQHandler(void)
{
	HAL_SPI_IRQHandler(&hspi);
}

__attribute__((isr)) void DMA_Channel0_IRQHandler(void)
{
	HAL_DMA_IRQHandler(&hdma_spi_tx);
}

其他改动

在这里插入图片描述

在这里插入图片描述

实验现象

在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【联盛德W806上手笔记】八、SPI及其DMA 的相关文章

  • DMA的补充笔记

    DMA有两个总线 xff1a 1 DMA存储器总线 xff1a DMA通过该总线来执行存储器数据的传入和传出 2 DMA外设总线 xff1a DMA通过该总线访问AHB外设 xff08 AHB主要是针对高效率 高频宽以及快速系统模块所设计的
  • STM32 DMA正常模式等待传输完成和开始下一次传输

    选择DMA的正常模式 xff0c 即DMA只传输一次 如果当传输完一次后 xff0c 还想再传输一次 xff0c 就需要重启DMA xff1a DMA Cmd DMA1 Channel6 DISABLE 重新设置源地址 重新设置目的地址 重
  • SPI通讯协议介绍

    来到SPI通讯协议了 废话两句 xff0c SPI很重要 xff0c 这是我在学校时候听那些单片机开发工程师说的 出来实习 xff0c 到后来工作 xff0c 确实如此 xff0c SPI的使用很常见 xff0c 那么自然重要咯 SPI S
  • stm32HAL库 串口接收不定长数据(DMA传输)

    相信大家很多初学者都会遇到串口接收不定长数据的情况 对于初学者可能看着有点难理解 xff0c 多看几遍就好 xff0c 亲测能用 话不多说上菜上菜 xff01 xff01 xff01 xff01 此代码是本人在具体工程应用 xff0c 实测
  • 在EBAZ4205 zynq7010上运行AXI_DMA中断回环测试

    在EBAZ4205 zynq7010上运行AXI DMA loop interrupt 整体的布局图 这是上面的一张接口图 下面对每个模块附上截图 AXI DMA的输出mm2s introut s2mm introut接到PS系统的中断触发
  • 【STM32】HAL库-SPI

    3线全双工同步传输 带或不带第三根双向数据线的双线单工同步传输 8或16位传输帧格式选择 主或从操作 支持多主模式 8个主模式波特率预分频系数 最大为fPCLK 2 从模式频率 最大为fPCLK 2 主模式和从模式的快速通信 主模式和从模式
  • 16. GD32F103C8T6入门教程-adc 使用教程2-dma+连续扫描方式采集数据

    adc 使用教程2 dma 连续扫描方式采集数据 adc 的扫描模式就是把配置了规则或注入通道按照配置的顺序采集一轮 adc 的连续转换模式就是把配置了规则或注入通道按照配置的顺序采集N轮 注意 dma使用时存在一个外设映射到一个dam外设
  • 沁恒CH32V307使用记录:SPI基础使用

    文章目录 目的 基础说明 使用演示 其它补充 总结 目的 SPI是单片机中比较常用的一个功能 这篇文章将对CH32V307中相关内容进行说明 本文使用沁恒官方的开发板 CH32V307 EVT R1沁恒RISC V模块MCU赤兔评估板 进行
  • 基于32单片机的16通道ADC的数据采集

    基于32单片机的16通道ADC的数据采集 这个部分的内容 是作为外部模拟量部分的采集工作 按照任务要求 所设计的方案 需要完成以下指标 ADC必须能采集16通道的模拟量 ADC的分辨率是16bit 采样率不小于20khz 由此分析可以得出
  • stm32之ADC应用实例(单通道、多通道、基于DMA)

    硬件 STM32F103VCT6 开发工具 Keil uVision4 下载调试工具 ARM仿真器 网上资料很多 这里做一个详细的整合 也不是很详细 但很通俗 所用的芯片内嵌3个12位的模拟 数字转换器 ADC 每个ADC共用多达16个外部
  • 外设驱动库开发笔记22:ADXL345三轴数字加速度计驱动

    移动设备的广泛应用增加对移动过程中各种参数的检测需求 ADXL345三轴数字加速度计可以用来检测加速度 进而测量倾斜角度等 在这一篇中 我们将讨论ADXL345三轴数字加速度计驱动程序的设计与实现 1 功能概述 ADXL345是一款小而薄的
  • Java SPI机制

    一 SPI机制简介 SPI的全名为Service Provider Interface java spi机制的思想 系统里抽象的各个模块 往往有很多不同的实现方案 在面向的对象的设计里 一般推荐模块之间基于接口编程 模块之间不对实现类进行硬
  • micropython-SPI通讯

    micropython SPI通讯 1 什么是SPI 2 SPI通讯原理 3 Micropython中的SPI 4 ZTMR测试SPI 1 ZTMR中SPI引脚 2 ZTMRSPI自测 2 SPI 2板之间通讯测试 1 什么是SPI SPI
  • spi,ClassLoader,双亲委托模式

    转载 https www cnblogs com hiyujie p wo xueJava1ClassLoader yu shuang qin wei tuo mo sh html 1 ClassLoader分类 Java虚拟机会创建三类C
  • I2C与SPI通信总线协议

    仅以寄存器地址为8Bit的器件为例 例如MPU6500 LSM6DS3 I2C通信协议 I2C 的要点是了解I2C通信帧的组成部分 START起始位 STOP停止位 ACK NACK信号 从机器件地址 从机寄存器地址 I2C读的时序比较繁琐
  • GD32F4xx MCU ADC+DMA 多通道采样

    1 GD32F4xx ADC GD32F4xx 的12位ADC是一种采用逐次逼近方式的模拟数字转换器 1 1 主要特征 可配置12位 10位 8位 6位分辨率 ADC采样率 12位分辨率为2 6MSPs 10位分辨率为3 0 MSPs 分辨
  • DMA 与中断驱动的 I/O

    我不太清楚 DMA 和中断 I O 之间的区别 当前正在阅读操作系统概念 第 7 版 具体来说 我不确定在这两种情况下何时会发生中断 以及在这两种情况下 CPU 在什么时候可以自由地执行其他工作 我一直在读但不一定能调和的东西 中断驱动 通
  • Linux内核设备驱动程序以DMA方式进入内核空间

    LDD3 p 453 演示dma map single使用作为参数传入的缓冲区 bus addr dma map single dev gt pci dev gt dev buffer count dev gt dma dir Q1 这个缓
  • Linux 内核中的 DMA 映射和 DMA 引擎是什么?

    Linux 内核中的 DMA 映射和 DMA 引擎是什么 DMA映射API和DMA引擎API何时可以在Linux设备驱动程序中使用 任何真正的 Linux 设备驱动程序示例作为参考都会很棒 Linux 内核中的 DMA 映射和 DMA 引擎
  • 如何用 C 语言从串行(SPI)连接读取数据?

    我正在尝试编写一个程序 该程序将安装在 Linux MCU Raspberry Pi 上 该程序将读取从另一个 MCU 我将自己构建的自制程序 发送到它的串行数据 我研究了如何做到这一点 并认为我有 大局 但仍然缺少一些东西 其一 我需要启

随机推荐