IMX8QXP Cortex-M4 SPI主从模式测试,利用内部的单片机M4核接收SPI数据进行测试。
环境准备
下载NXP MCU SDK,下载编译链,参考韬睿文章NXP iMX8X M4核心SPI开发,使用IMX8QXP demo板,M4核spi3做主模式,A35核spi0做从模式,飞线连接:
M4代码修改
直接在freertos_hello测试代码中修改。
修改编译配置文件:
devices/MIMX8QX6/drivers/driver_lpspi_freertos_MIMX8QX6.cmake
if(NOT DRIVER_LPUSPI_FREERTOS_MIMX8QX6_INCLUDED)
set(DRIVER_LPSPI_FREERTOS_MIMX8QX6_INCLUDED true CACHE BOOL "driver_lpspi_freertos component is included.")
target_sources(${MCUX_SDK_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/fsl_lpspi_freertos.c
)
target_include_directories(${MCUX_SDK_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
)
include(driver_lpspi_MIMX8QX6)
include(middleware_freertos-kernel_MIMX8QX6)
endif()
devices/MIMX8QX6/drivers/driver_lpspi_MIMX8QX6.cmake
if(NOT DRIVER_LPSPI_MIMX8QX6_INCLUDED)
set(DRIVER_LPSPI_MIMX8QX6_INCLUDED true CACHE BOOL "driver_lpspi component is included.")
target_sources(${MCUX_SDK_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/fsl_lpspi.c
)
target_include_directories(${MCUX_SDK_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/.
)
include(driver_common_MIMX8QX6)
endif()
boards/mekmimx8qx/rtos_examples/freertos_hello/armgcc/CMakeLists.txt
include(driver_lpspi_MIMX8QX6)
include(driver_lpspi_freertos_MIMX8QX6)
修改代码文件:
pin_mux.h
/* ADC_IN2 (coord V32), M40_UART0_RX */
#define BOARD_INITPINS_M40_UART0_RX_PIN_FUNCTION_ID SC_P_ADC_IN2 /*!< Pin function id */
/* ADC_IN3 (coord V30), M40_UART0_TX */
#define BOARD_INITPINS_M40_UART0_TX_PIN_FUNCTION_ID SC_P_ADC_IN3 /*!< Pin function id */
#define BOARD_INITPINS_SPI3_MOSI_PIN_FUNCTION_ID SC_P_SPI3_SDO
#define BOARD_INITPINS_SPI3_MISO_PIN_FUNCTION_ID SC_P_SPI3_SDI
#define BOARD_INITPINS_SPI3_CLK_PIN_FUNCTION_ID SC_P_SPI3_SCK
#define BOARD_INITPINS_SPI3_CS0_PIN_FUNCTION_ID SC_P_SPI3_CS0
pin_mux.c
void BOARD_InitPins(sc_ipc_t ipc) /*!< Function assigned for the core: Cortex-M4F[m4] */
{
sc_err_t err = SC_ERR_NONE;
err = sc_pad_set_all(ipc, BOARD_INITPINS_M40_UART0_RX_PIN_FUNCTION_ID, 1U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);/* IOMUXD_ADC_IN2 register modification value */
if (SC_ERR_NONE != err)
{
assert(false);
}
err = sc_pad_set_all(ipc, BOARD_INITPINS_M40_UART0_TX_PIN_FUNCTION_ID, 1U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);/* IOMUXD_ADC_IN3 register modification value */
if (SC_ERR_NONE != err)
{
assert(false);
}
err = sc_pad_set_all(ipc, BOARD_INITPINS_SPI3_MOSI_PIN_FUNCTION_ID, 0U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);
if (SC_ERR_NONE != err)
{
assert(false);
}
err = sc_pad_set_all(ipc, BOARD_INITPINS_SPI3_MISO_PIN_FUNCTION_ID, 0U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);
if (SC_ERR_NONE != err)
{
assert(false);
}
err = sc_pad_set_all(ipc, BOARD_INITPINS_SPI3_CLK_PIN_FUNCTION_ID, 0U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);
if (SC_ERR_NONE != err)
{
assert(false);
}
err = sc_pad_set_all(ipc, BOARD_INITPINS_SPI3_CS0_PIN_FUNCTION_ID, 0U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);
if (SC_ERR_NONE != err)
{
assert(false);
}
}
freertos_hello.c
/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2017 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* FreeRTOS kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"
/* Freescale includes. */
#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_lpuart.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#include "fsl_lpspi_freertos.h"
lpspi_rtos_handle_t handle;
lpspi_master_config_t masterConfig;
#define LPUART_CLK_FREQ CLOCK_GetIpFreq(kCLOCK_DMA_Lpspi3)
uint8_t send_buffer[128];
uint8_t recv_buffer[128];
lpspi_transfer_t spi_data = {
.txData = send_buffer,
.rxData = recv_buffer,
.dataSize = sizeof(send_buffer),
.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap,
};
/* Task priorities. */
#define hello_task_PRIORITY (configMAX_PRIORITIES - 1)
/*******************************************************************************
* Prototypes
******************************************************************************/
static void hello_task(void *pvParameters);
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Application entry point.
*/
int main(void)
{
/* Init board hardware. */
sc_ipc_t ipc;
ipc = BOARD_InitRpc();
BOARD_InitPins(ipc);
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
BOARD_InitMemory();
//配置 LPSPI 的供电
if (sc_pm_set_resource_power_mode(ipc, SC_R_SPI_3, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
{
PRINTF("Error: Failed to power on SPI3\r\n");
}
//设置 LPSPI 时钟源
sc_pm_clock_enable(ipc, SC_R_SPI_3, SC_PM_CLK_PER, true, 0);
if (CLOCK_SetIpFreq(kCLOCK_DMA_Lpspi3, SC_60MHZ) == 0)
{
PRINTF("Error: Failed to set SPI3 frequency\r\n");
}
PRINTF("LPUART_CLK_FREQ=%d.\r\n",LPUART_CLK_FREQ);
//获取 LPSPI 默认配置
LPSPI_MasterGetDefaultConfig(&masterConfig);
//masterConfig.baudRate = 500000;//Default 500000 500K
//masterConfig.bitsPerFrame = 8;//Default 8bit,16 is ok
//完成对 LPSPI 工作状态配置,包括 SPI 时钟频率、相位、采样点、帧长等,这些包含在 lpspi_config 结构体中
if (kStatus_Success != LPSPI_RTOS_Init(&handle, ADMA__LPSPI3, &masterConfig, LPUART_CLK_FREQ))
{
PRINTF("Failed to init SPI3.\r\n");
vTaskSuspend(NULL);
}
if (xTaskCreate(hello_task, "Hello_task", configMINIMAL_STACK_SIZE + 100, NULL, hello_task_PRIORITY, NULL) !=
pdPASS)
{
PRINTF("Task creation failed!.\r\n");
while (1)
;
}
vTaskStartScheduler();
for (;;)
;
}
/*!
* @brief Task responsible for printing of "Hello world." message.
*/
static void hello_task(void *pvParameters)
{
uint8_t config_cmds[32];
uint8_t sizes = 8;
uint32_t cnt = 0;
config_cmds[0] = 0x1;
config_cmds[1] = 0x2;
config_cmds[2] = 0x3;
config_cmds[3] = 0x4;
config_cmds[4] = 0x5;
config_cmds[5] = 0x6;
config_cmds[6] = 0x7;
config_cmds[7] = 0x8;
config_cmds[8] = 0;
memcpy(send_buffer, config_cmds, sizes);
spi_data.dataSize = sizes;
PRINTF("\r\n#################### spi3 master test ####################\n\r\n");
PRINTF(" Build Time: %s--%s \n\r\n", __DATE__, __TIME__);
PRINTF("##########################################################\r\n");
for (;;)
{
//延时1秒
SDK_DelayAtLeastUs(1000000, SystemCoreClock);
if (kStatus_Success != LPSPI_RTOS_TransferBlocking(&handle, &spi_data))//自构建阻塞,可以发送数据
//if (kStatus_Success != LPSPI_RTOS_Transfer(&handle, &spi_data))//默认非阻塞,不能发送数据卡在函数里面
{
PRINTF("Command Transmission fails.\r\n");
vTaskSuspend(NULL);
}
PRINTF("send %d ok.\r\n",++cnt);
//vTaskSuspend(NULL);
}
}
void LPSPI_MasterGetDefaultConfig(lpspi_master_config_t *masterConfig)
{
assert(masterConfig != NULL);
/* Initializes the configure structure to zero. */
(void)memset(masterConfig, 0, sizeof(*masterConfig));
masterConfig->baudRate = 500000;
masterConfig->bitsPerFrame = 8;
masterConfig->cpol = kLPSPI_ClockPolarityActiveHigh;
masterConfig->cpha = kLPSPI_ClockPhaseFirstEdge;
masterConfig->direction = kLPSPI_MsbFirst;
masterConfig->pcsToSckDelayInNanoSec = 1000000000U / masterConfig->baudRate * 2U;
masterConfig->lastSckToPcsDelayInNanoSec = 1000000000U / masterConfig->baudRate * 2U;
masterConfig->betweenTransferDelayInNanoSec = 1000000000U / masterConfig->baudRate * 2U;
masterConfig->whichPcs = kLPSPI_Pcs0;
masterConfig->pcsActiveHighOrLow = kLPSPI_PcsActiveLow;
masterConfig->pinCfg = kLPSPI_SdiInSdoOut;
masterConfig->dataOutConfig = kLpspiDataOutRetained;
}
devices/MIMX8QX6/drivers/fsl_lpspi_freertos.c
status_t LPSPI_RTOS_TransferBlocking(lpspi_rtos_handle_t *handle, lpspi_transfer_t *transfer)
{
status_t status;
status = LPSPI_MasterTransferBlocking(handle->base, transfer);
if (status != kStatus_Success)
{
return status;
}
return status;
}
devices/MIMX8QX6/drivers/fsl_lpspi_freertos.h
status_t LPSPI_RTOS_TransferBlocking(lpspi_rtos_handle_t *handle, lpspi_transfer_t *transfer);
A35代码修改
fsl-imx8qxp-mek.dtsi,打开spi0从模式,spi3注释关闭。
pinctrl_lpspi0: lpspi0grp {
fsl,pins = <
SC_P_SPI0_SCK_ADMA_SPI0_SCK 0x0600004c
SC_P_SPI0_SDO_ADMA_SPI0_SDO 0x0600004c
SC_P_SPI0_SDI_ADMA_SPI0_SDI 0x0600004c
SC_P_SPI0_CS0_ADMA_SPI0_CS0 0x0600004c
>;
};
&lpspi0 {
#address-cells = <1>;
#size-cells = <0>;
fsl,spi-num-chipselects = <1>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lpspi0>;
spi-slave;
status = "okay";
};
运行测试
编译freertos_hello,M4中tftp下载运行,然后boot启动A35进入shell界面,运行内核和用户测试程序:
上面测试结果是M4的spi3做主模式,从模式待研究。
使用下面韬睿的代码设置lpspi_config,不能发送数据,cpol和cpha有差异:
lpspi_master_config_t lpspi_config = {
.baudRate = 6000000,
.bitsPerFrame = 1024, /*!< Bits per frame, minimum 8, maximum 4096.*/
.cpol = kLPSPI_ClockPolarityActiveLow,
.cpha = kLPSPI_ClockPhaseSecondEdge,
.direction = kLPSPI_MsbFirst,
.pcsToSckDelayInNanoSec = 50,
.lastSckToPcsDelayInNanoSec = 50,
.betweenTransferDelayInNanoSec = 50,
.whichPcs = kLPSPI_Pcs0,
.pcsActiveHighOrLow = kLPSPI_PcsActiveLow,
.pinCfg = kLPSPI_SdiInSdoOut,
.dataOutConfig = kLpspiDataOutRetained,
};
默认的LPSPI_RTOS_Transfer函数不能发送数据问题,设置.rxData = NULL,发送一包数据后,不能退出发送状态,卡在xSemaphoreTake函数:
status_t LPSPI_RTOS_Transfer(lpspi_rtos_handle_t *handle, lpspi_transfer_t *transfer)
{
status_t status;
/* Lock resource mutex */
if (xSemaphoreTake(handle->mutex, portMAX_DELAY) != pdTRUE)
{
return kStatus_LPSPI_Busy;
}
status = LPSPI_MasterTransferNonBlocking(handle->base, &handle->drv_handle, transfer);
if (status != kStatus_Success)
{
(void)xSemaphoreGive(handle->mutex);
return status;
}
/* Wait for transfer to finish */
if (xSemaphoreTake(handle->event, portMAX_DELAY) != pdTRUE)
{
return kStatus_LPSPI_Error;
}
/* Unlock resource mutex */
(void)xSemaphoreGive(handle->mutex);
/* Return status captured by callback function */
return handle->async_status;
}
这是为何呢?
参考master模式修改为slave模式,接收数据,第一次没有收到数据,第二次以后返回busy状态,改为阻塞还是busy,LPSPI_GetStatusFlags
LPSPI_SlaveTransferNonBlocking
/* Check that we're not busy.*/
if (handle->state == (uint8_t)kLPSPI_Busy)
{
PRINTF("error lpspi busy.\r\n");
return kStatus_LPSPI_Busy;
}