打印
[其它]

AT32F437 的SPI全双工传输数据中,DMA中断问题(已解决)

[复制链接]
1493|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
laosm|  楼主 | 2022-7-17 16:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 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);      
  }
}

使用特权

评论回复
沙发
dcxq13| | 2022-7-18 14:05 | 只看该作者
mark等待大神解答,我用AT32F403A也是做SPI DMA通讯,但是没成功,不进DMA中断

使用特权

评论回复
板凳
laosm|  楼主 | 2022-7-24 14:31 | 只看该作者
问题已经解决。
错误原因是SPI的TDBE和RDBF与DMA的启动时序不对。应该先使能DMA,然后使能SPI,就可以正确地实现DMA中断了。
启动DMA中断传送的新函数如下:
/******************************************************************
*                                        SPI1-DMA 启动传送
* 入口:        SendBuff  指向要发送数据的起始地址的指针
                                SendNumb  要发送的字节数
* 出口:无
* 使用:SPI1,要发送的数据在 SendBuff, 接收的数据在SPI_Receiver_Buff
*/
void SPI1_Dma_Send(uint32_t SendBuff, uint16_t SendNumb)
{
        spi_enable(SPI1, FALSE);                                                                //停止SPI
        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);
        spi_enable(SPI1, TRUE);                                                                         //使能SPI
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

10

帖子

0

粉丝