本帖最后由 laosm 于 2022-7-25 09:24 编辑
在我的工程中,用AT32F437VGT7的SPI1全双工传输数据,并使用 DMA1 进行发送数据的写入和接收数据的读取,启用DMA1的中断功能。
发现DMA的接收完成中断,并不按照设想的规律进行,而是在SPI数据没传送完的情况下,就发生了中断。
我用DMA1_CHANNEL1发送字节数据,DMA1_CHANNEL2接收字节数据,数据长度为2字节,设置DMA1_CHANNEL2的传送完毕产生中断。
SPI1的片选信号CS用软件控制,先将CS置低电平,然后使能DMA传输;等待DMA1_CHANNEL2的接收中断,在中断服务中置CS为高电平。周期性地重复这一过程。
以下是中断服务程序:
/*******************************************************
* SPI1-DMA1 中断
*/
void DMA1_Channel2_IRQHandler(void)
{
if(dma_flag_get(DMA1_FDT2_FLAG) != RESET)
{
dma_flag_clear(DMA1_FDT2_FLAG); //清除Channe2中断标志
//复位CS
Set_SPI_CS(FALSE);
}
}
运行程序,用示波器同步观察CS和Sclk, 发现数据传输不到16位时,CS信号就复位,也就是说数据传输过程还没有完成,就发生了DMA接收完成中断!
测试了一下,这个提前量似乎还与SPI的SCLK频率有关:
频率1.88MHz时,CS在第9个脉冲的上升沿复位;
频率3.75MHz时,CS在第10个脉冲的上升沿复位;
频率7.50MHz时,CS在第12个脉冲的下降沿复位;
频率15.0MHz时,CS在第15个脉冲的下降沿复位。
反复检查,没有找到问题所在,最后在中断服务程序里加了一行,等待SPI接收完成:
while(spi_i2s_flag_get(SPI1, SPI_I2S_RDBF_FLAG) == RESET){;}
这样,在16位数据传送完后,CS信号才会复位,SPI终于能正常工作了。可是浪费了查询等待的时间。
这究竟是什么原因造成的?是设置不正确?或是DMA底层的逻辑有问题?请有兴趣的网友帮忙分析一下。
贴上有关的初始化程序:
/*************初始化SPI DMA ******************/
//初始化SPI1 PA5 sclk;PA6 MISO; PB5 MOSI
//GPIO 时钟已经在BSP中使能
//PA5 SCK 初始化
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_DOWN;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_5;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_5); //MUX5 -> SPI1_SCK
//PA6 MISO
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_pins = GPIO_PINS_6;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE6, GPIO_MUX_5); //MUX5 -> SPI1_MISO
//PB5 MOSI
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_pins = GPIO_PINS_5;
gpio_init(GPIOB, &gpio_initstructure);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE5, GPIO_MUX_5); //MUX5 -> SPI1_MOSI
// 初始化 DMA1
crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
dma_reset(DMA1_CHANNEL1);
dma_reset(DMA1_CHANNEL2);
dmamux_enable(DMA1, TRUE);
dmamux_init(DMA1MUX_CHANNEL1, DMAMUX_DMAREQ_ID_SPI1_TX); //发送
dmamux_init(DMA1MUX_CHANNEL2, DMAMUX_DMAREQ_ID_SPI1_RX); //接收
dma_default_para_init(&dma_init_struct);
dma_init_struct.buffer_size = 4U; //缓存4字节
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
dma_init_struct.memory_inc_enable = TRUE;
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init_struct.loop_mode_enable = FALSE;
dma_init_struct.memory_base_addr = (uint32_t)SPI_Cmd_Buff; //这个地址是临时的,实际工作时另行赋值
dma_init_struct.peripheral_base_addr = (uint32_t)&(SPI1->dt);
dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
dma_init(DMA1_CHANNEL1, &dma_init_struct);
dma_init_struct.memory_base_addr = (uint32_t)SPI_Receiver_Buff; //接收的数据在这里
dma_init_struct.peripheral_base_addr = (uint32_t)&(SPI1->dt);
dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
dma_init(DMA1_CHANNEL2, &dma_init_struct);
// dma1 channel2 interrupt nvic init
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(DMA1_Channel2_IRQn, 2, 0);
dma_interrupt_enable(DMA1_CHANNEL2, DMA_FDT_INT, TRUE); //当接收完毕时中断
//SPI1 设置,SPI1 全双工,8位数据传送,DMA
crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE); //使能SPI1 时钟
spi_default_para_init(&spi_init_struct);
spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_32; //120MHz / 32 = 3.75MHz
spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW;
spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE;
spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;
spi_init(SPI1, &spi_init_struct);
//使能SPI的DMA传送
spi_i2s_dma_transmitter_enable(SPI1,TRUE);
spi_i2s_dma_receiver_enable(SPI1,TRUE);
spi_enable(SPI1, TRUE);
以下是启动传输的函数,它被main函数周期调用,并在调用之前已将CS置低:
/******************************************************************
* SPI1-DMA 启动传送
* 入口: SendBuff 要发送数据的起始地址
SendNumb 要发送的字节数
* 出口:无
* 使用:SPI1,DMA1; 接收的数据在SPI_Receiver_Buff(在初始化时指定的)
*/
void SPI1_DMA_Send(uint32_t SendBuff, uint16_t SendNumb)
{
dma_channel_enable (DMA1_CHANNEL1, FALSE);
dma_channel_enable (DMA1_CHANNEL2, FALSE);
DMA1_CHANNEL1->maddr = SendBuff; //设置地址
DMA1_CHANNEL1->dtcnt_bit.cnt = SendNumb; //设置数量
DMA1_CHANNEL2->dtcnt_bit.cnt = SendNumb;
dma_channel_enable(DMA1_CHANNEL1, TRUE);
dma_channel_enable(DMA1_CHANNEL2, TRUE);
}
新的中断服务程序:
/*******************************************************
* SPI1-DMA1 中断
*/
void DMA1_Channel2_IRQHandler(void)
{
if(dma_flag_get(DMA1_FDT2_FLAG) != RESET)
{
while(spi_i2s_flag_get(SPI1, SPI_I2S_RDBF_FLAG) == RESET){;} //等待传输完成
dma_flag_clear(DMA1_FDT2_FLAG); //清除Channe2中断标志
//复位CS
Set_SPI_CS(FALSE);
}
}
|