我想使用STM32F446RE-Nucleo电路板以常规频率读出MCP3201 ADC。我想使用SPI和DMA与ADC进行通信,将数据直接写入存储器。
STM32F446RE的代码(如下所示)结构如下:
SPI正确生成一个16周期时钟信号,但CS信号过早拉高,如示波器上所示(顶部时钟信号,底部CS信号):
我做错了什么导致DMA1_Stream3_IRQHandler在2位之后而不是在接收到所有16位数据之后被调用?
#include "main.h"
#define SYS_CLK_FREQ 100000000 //100 MHz
#define SYSTICK_FREQ 18000 //18 kHz
#define ADC_SCK_PIN LL_GPIO_PIN_13 //PB13
#define ADC_MISO_PIN LL_GPIO_PIN_14 //PB14
#define ADC_CS_PIN LL_GPIO_PIN_15 //PB15
#define BUFFER_SIZE 16 //RX Buffer size
__IO uint32_t uwMeasuredFrequency = 0; //measured freq
static uint32_t TimOutClock = 1; // TIM2 Clock
static uint8_t bit = 0;
static uint8_t initFinished = 0;
uint16_t aRxBuffer[BUFFER_SIZE];
// Private function prototypes
__STATIC_INLINE void SystemClock_Config(void);
__STATIC_INLINE void Configure_Frequency(uint32_t Frequency);
__STATIC_INLINE void LED_Init(void);
__STATIC_INLINE void LED_Blinking(uint32_t Period);
__STATIC_INLINE void UserButton_Init(void);
__STATIC_INLINE void Configure_SPI2 (void);
__STATIC_INLINE void Configure_DMA1 (void);
void DMA1_ReceiveComplete_Callback (void);
/** @brief Main program */
int main(void) {
SystemClock_Config(); // Configure the system clock to 100 MHz
LED_Init(); // Initialize LED2
UserButton_Init(); // Initialize button in EXTI mode
Configure_SPI2 ();
Configure_DMA1 ();
initFinished = 1;
while (1) {}
}
/**
* @brief Periodic call, using the SysTick handler. This starts communication with the ADC, that will
* send a data sample.
* @param None
* @retval None
*/
void SysTick_Handler (void)
{
//LL_GPIO_SetOutputPin (LED2_GPIO_PORT, LED2_PIN);
/* Activate SPI 2 */
if (initFinished) {
LL_GPIO_ResetOutputPin (GPIOB, ADC_CS_PIN); //Pull CS low, to start ADC sampling
LL_SPI_Enable (SPI2);
LL_DMA_EnableStream (DMA1, LL_DMA_STREAM_3);
}
}
/**
* @brief Function called when a sample has been received from the ADC. It can be stored or processed.
* @param None
* @retval None
*/
void DMA1_ReceiveComplete_Callback (void) {
LL_DMA_DisableStream (DMA1, LL_DMA_STREAM_3);
LL_SPI_Disable (SPI2);
uint16_t sample = aRxBuffer[0];
//LL_GPIO_SetOutputPin (GPIOB, ADC_CS_PIN);
LL_GPIO_TogglePin (GPIOB, ADC_CS_PIN);
}
/**
* @brief This function configures SPI2.
* @note This function is used to :
* -1- Enables GPIO clock and configures the SPI2 pins.
* -2- Configure SPI2 functional parameters.
* @note Peripheral configuration is minimal configuration from reset values.
* Thus, some useless LL unitary functions calls below are provided as
* commented examples - setting is default configuration from reset.
* @param None
* @retval None
*/
__STATIC_INLINE void Configure_SPI2 (void) {
/* (1) Enables GPIO clock and configures the SPI2 pins ********************/
/* Enable the peripheral clock of GPIOB */
LL_AHB1_GRP1_EnableClock (LL_AHB1_GRP1_PERIPH_ALL); //(LL_AHB1_GRP1_PERIPH_GPIOB) ;
/* Configure SCK Pin connected to pin PB13 of CN10 connector */
LL_GPIO_SetPinMode (GPIOB, ADC_SCK_PIN, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15 (GPIOB, ADC_SCK_PIN, LL_GPIO_AF_5);
LL_GPIO_SetPinSpeed (GPIOB, ADC_SCK_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinPull (GPIOB, ADC_SCK_PIN, LL_GPIO_PULL_DOWN);
/* Configure MISO Pin connected to pin PB14 of CN10 connector */
LL_GPIO_SetPinMode (GPIOB, ADC_MISO_PIN, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15 (GPIOB, ADC_MISO_PIN, LL_GPIO_AF_5);
LL_GPIO_SetPinSpeed (GPIOB, ADC_MISO_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinPull (GPIOB, ADC_MISO_PIN, LL_GPIO_PULL_DOWN);
/* Configure CS Pin connected to pin PB15*/
LL_GPIO_SetPinMode (GPIOB, ADC_CS_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinSpeed (GPIOB, ADC_CS_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinPull (GPIOB, ADC_CS_PIN, LL_GPIO_PULL_NO);
/* (2) Configure SPI2 functional parameters ********************************/
/* Enable the peripheral clock of GPIOB */
LL_APB1_GRP1_EnableClock (LL_APB1_GRP1_PERIPH_SPI2);
/* Configure SPI2 communication */
LL_SPI_SetBaudRatePrescaler (SPI2, LL_SPI_BAUDRATEPRESCALER_DIV64);
LL_SPI_SetTransferDirection (SPI2, LL_SPI_SIMPLEX_RX);
LL_SPI_SetClockPhase (SPI2, LL_SPI_PHASE_2EDGE);
LL_SPI_SetClockPolarity (SPI2, LL_SPI_POLARITY_LOW);
LL_SPI_SetTransferBitOrder (SPI2, LL_SPI_MSB_FIRST); //already the default value
LL_SPI_SetDataWidth (SPI2, LL_SPI_DATAWIDTH_16BIT);
LL_SPI_SetNSSMode (SPI2, LL_SPI_NSS_SOFT);
LL_SPI_SetMode (SPI2, LL_SPI_MODE_MASTER); //I am the master! Give me samples!
/* Enable DMA RX Interrupt */
LL_SPI_EnableDMAReq_RX (SPI2);
}
/**
* @brief This function configures the DMA1 Channels for SPI2
* @note This function is used to :
* -1- Enable DMA1 clock
* -2- Configure NVIC for DMA1 transfer complete/error interrupts
* -3- Configure the DMA1_Stream3 functional parameters
* -4- Enable DMA1 interrupts complete/error
* @param None
* @retval None
*/
__STATIC_INLINE void Configure_DMA1 (void) {
// DMA1 used for SPI2 Reception
LL_AHB1_GRP1_EnableClock (LL_AHB1_GRP1_PERIPH_DMA1); // Enable DMA1-clock
/* (2) Configure NVIC for DMA transfer complete/error interrupts */
NVIC_SetPriority (DMA1_Stream3_IRQn, 0);
NVIC_EnableIRQ (DMA1_Stream3_IRQn);
/* (4) Configure the DMA1_Stream3 functional parameters */
LL_DMA_ConfigTransfer (DMA1,
LL_DMA_STREAM_3,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL |
LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT |
LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE);
LL_DMA_ConfigAddresses (DMA1,
LL_DMA_STREAM_3,
LL_SPI_DMA_GetRegAddr (SPI2),
(uint32_t)aRxBuffer,
LL_DMA_GetDataTransferDirection (DMA1, LL_DMA_STREAM_3));
LL_DMA_SetDataLength (DMA1, LL_DMA_STREAM_3, 1); //We receive 1 sample (16-bit)
LL_DMA_SetChannelSelection (DMA1, LL_DMA_STREAM_3, LL_DMA_CHANNEL_0);
/* (5) Enable DMA interrupts complete/error */
LL_DMA_EnableIT_TC (DMA1, LL_DMA_STREAM_3);
LL_DMA_EnableIT_TE (DMA1, LL_DMA_STREAM_3);
}
/**
* @brief Initialize LED2.
* @param None
* @retval None
*/
__STATIC_INLINE void LED_Init(void) {
LED2_GPIO_CLK_ENABLE();
LL_GPIO_SetPinMode(LED2_GPIO_PORT, LED2_PIN, LL_GPIO_MODE_OUTPUT);
}
/**
* @brief Configures User push-button in GPIO or EXTI Line Mode.
*/
__STATIC_INLINE void UserButton_Init(void) {
USER_BUTTON_GPIO_CLK_ENABLE();
LL_GPIO_SetPinMode(USER_BUTTON_GPIO_PORT, USER_BUTTON_PIN, LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(USER_BUTTON_GPIO_PORT, USER_BUTTON_PIN, LL_GPIO_PULL_NO);
USER_BUTTON_SYSCFG_SET_EXTI();
USER_BUTTON_EXTI_LINE_ENABLE();
USER_BUTTON_EXTI_FALLING_TRIG_ENABLE();
NVIC_EnableIRQ(USER_BUTTON_EXTI_IRQn);
NVIC_SetPriority(USER_BUTTON_EXTI_IRQn,0x03);
}
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 100000000
* HCLK(Hz) = 100000000
* AHB Prescaler = 1
* APB1 Prescaler = 2
* APB2 Prescaler = 1
* HSE Frequency(Hz) = 8000000
* PLL_M = 8
* PLL_N = 400
* PLL_P = 4
* VDD(V) = 3.3
* Main regulator output voltage = Scale1 mode
* Flash Latency(WS) = 3
* @param None
* @retval None
*/
void SystemClock_Config(void) {
/* Enable HSE oscillator */
LL_RCC_HSE_EnableBypass();
LL_RCC_HSE_Enable();
while(LL_RCC_HSE_IsReady() != 1) {};
LL_FLASH_SetLatency(LL_FLASH_LATENCY_3);
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_8, 400, LL_RCC_PLLP_DIV_4);
LL_RCC_PLL_Enable();
while(LL_RCC_PLL_IsReady() != 1) {}
/* Sysclk activation on the main PLL */
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
while (LL_RCC_GetSysClkSource () != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {}
/* Set APB1 & APB2 prescaler */
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
SysTick_Config (SYS_CLK_FREQ / SYSTICK_FREQ); // Set sysTick
/* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
SystemCoreClock = SYS_CLK_FREQ;
}
/******************************************************************************/
/* USER IRQ HANDLER TREATMENT */
/******************************************************************************/
/**
* @brief User button interrupt processing
* @note When the user key button is pressed the frequency of the
* PWM signal generated by TIM2 is updated.
* @param None
* @retval None
*/
void UserButton_Callback(void)
{
/* please do nothing yet*/
}
/**
* @brief This function handles external line 13 interrupt request.
* @param None
* @retval None
*/
void USER_BUTTON_IRQHANDLER (void)
{
/* Manage Flags */
if (LL_EXTI_IsActiveFlag_0_31 (USER_BUTTON_EXTI_LINE) != RESET) {
LL_EXTI_ClearFlag_0_31 (USER_BUTTON_EXTI_LINE);
/* User button interrupt processing(function defined in main.c) */
UserButton_Callback ();
}
}
void DMA1_Stream3_IRQHandler (void)
{
if (LL_DMA_IsActiveFlag_TC3 (DMA1))
{
LL_DMA_ClearFlag_TC3 (DMA1);
/* Call function Reception complete Callback */
DMA1_ReceiveComplete_Callback ();
}
else if (LL_DMA_IsActiveFlag_TE3 (DMA1))
{
/* TODO Call Error function */
}
}