OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4672|回复: 6

stm32F1定时器捕获触发DMA通过SPI传输多个字节

[复制链接]

8

主题

42

帖子

0

精华

初级会员

Rank: 2

积分
161
金钱
161
注册时间
2014-1-7
在线时间
24 小时
发表于 2021-11-25 17:40:58 | 显示全部楼层 |阅读模式
1金钱
各位大大好,小弟要实现DMA自动采集ADC的数据,ADC是SPI接口的,ADC的INT引脚为低电平时代表数据有效,此时需要发送固定的15个字节数据来完成ADC读书和状态清除,
于是我就想到用stm32F103定时器TIM2 CH4输入捕获触发DMA,
DMA的外设地址设置SPI1->DR,内存地址设置为15个字节对应的数组地址,数据长度设置为15,
通过调试发现定时器每次下降沿只能通过SPI发送1个字节,并不能1此发送完15个字节,
请各位大神帮我看看是哪里设置的不对,谢谢!

下面附上相关程序
  1. 通用定时器2中断初始化
  2. //这里时钟选择为APB1的2倍,而APB1为36M
  3. //arr:自动重装值。
  4. //psc:时钟预分频数
  5. //这里使用的是定时器2!
  6. void TIM2_Int_Init(u16 arr,u16 psc)
  7. {
  8.     GPIO_InitTypeDef GPIO_InitStructure;
  9.     DMA_InitTypeDef DMA_InitStructure;
  10.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  11.         TIM_ICInitTypeDef  TIM2_ICInitStructure;
  12.        
  13.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能
  14.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
  15.                
  16.         GPIO_InitStructure.GPIO_Pin= GPIO_Pin_3;  
  17.         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  18.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;  //配置IO为上拉输入模式
  19.         GPIO_Init(GPIOA,&GPIO_InitStructure);   //把配置好的IO口初始化一下
  20.        
  21.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         计数到5000为500ms
  22.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
  23.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  24.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  25.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

  26. //        TIM_ITConfig(TIM2,TIM_IT_CC4,ENABLE ); //使能指定的TIM2中断,允许CC4捕获中断
  27.         TIM2_ICInitStructure.TIM_Channel = TIM_Channel_4; //CC1S=04        选择输入端 IC1映射到TI4上
  28.           TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;        //下降沿捕获
  29.           TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
  30.           TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;         //配置输入分频,不分频
  31.           TIM2_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
  32.           TIM_ICInit(TIM2, &TIM2_ICInitStructure);   
  33.         TIM_DMACmd(TIM2, TIM_DMA_CC4, ENABLE);                //使能或者失能指定的TIMx的DMA请求
  34.        
  35.         TIM_Cmd(TIM2,ENABLE);//开启定时器2
  36.     //启动DMA时钟
  37.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  38.     //DMA1通道3配置
  39.     DMA_DeInit(DMA1_Channel7);
  40.     //外设地址
  41.     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SPI1_DR_Addr;
  42.     //内存地址
  43.     DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff;       
  44.     //dma传输方向单向
  45.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;        //Memory_to_Peripheral
  46.     //设置DMA在传输时缓冲区的长度
  47.     DMA_InitStructure.DMA_BufferSize = SPI_TX_Len;
  48.     //设置DMA的外设递增模式,一个外设
  49.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  50.     //设置DMA的内存递增模式
  51.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  52.     //外设数据字长
  53.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  54.     //内存数据字长
  55.     DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
  56.     //设置DMA的传输模式
  57. //    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  58.         DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  59.     //设置DMA的优先级别
  60.     DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  61.     //设置DMA的2个memory中的变量互相访问
  62.     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  63.     DMA_Init(DMA1_Channel7,&DMA_InitStructure);   
  64.     DMA_Cmd(DMA1_Channel7,ENABLE);                        
  65. }


  66. /********************************************************************************************************
  67. ** 函数名称:
  68. ** 功能描述:
  69. ** 输   入:无
  70. ** 输   出:无
  71. ** 全局变量:无
  72. ** 调用模块:RCC_APB2PeriphClockCmd
  73.             RCC_APB1PeriphClockCmd
  74.             GPIO_Init
  75.             GPIO_SetBits
  76.             SPI_Init
  77. SPI_Cmd
  78. SPI2_RWByte   
  79. ********************************************************************************************************/
  80. void SPI1_Init(void)
  81. {
  82.         SPI_InitTypeDef  SPI_InitStructure;
  83.         GPIO_InitTypeDef GPIO_InitStructure;
  84.    
  85.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA,ENABLE);  
  86. //    //配置SPI1引脚:SCK, MISO and MOSI  
  87.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;  
  88.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP ;  
  89.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;  
  90.     GPIO_Init( GPIOA, &GPIO_InitStructure );
  91.     GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);  //PB5/6/7上拉
  92.     //SPI1配置  
  93.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //SPI设置为双线双向
  94.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                            //设置SPI工作模式:设置为主SPI
  95.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                        //设置SPI的数据大小:SPI发送接收8位帧结构
  96.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                          //选择了串行时钟空闲时为低电平
  97.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                        //数据捕获于时钟上升沿
  98.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                        //内部NSS信号有SSI位控制
  99.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;  //波特率预分频值为32(72M/32) 2.25MHz
  100.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                   //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
  101.         SPI_InitStructure.SPI_CRCPolynomial = 7;                            //CRC值计算的多项式
  102.         SPI_Init(SPI1, &SPI_InitStructure);                               //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
  103. //        SPI1->DR = 0xFF ;                                                                                                        //接送前读一次SPI1->DR,保证接收缓冲区为空
  104.     SPI1->CR2 |=0x03;                                                   //发送缓冲区DMA使能;//接收缓冲区DMA使能;

  105.         SPI_Cmd(SPI1, ENABLE);                                             //使能SPI外设
  106. }

  107. void SPI1_DMA_Configuration( void )
  108. {

  109.     RCC->AHBENR |= 1<<0 ;                     //DMA1时钟使能

  110.         /*------------------配置SPI1_RX_DMA通道Channel2---------------------*/

  111.     DMA1_Channel2->CCR &= ~( 1<<14 ) ;        //非存储器到存储器模式
  112.         DMA1_Channel2->CCR |=    2<<12   ;        //通道优先级高
  113.         DMA1_Channel2->CCR &= ~( 3<<10 ) ;        //存储器数据宽度8bit
  114.         DMA1_Channel2->CCR &= ~( 3<<8  ) ;        //外设数据宽度8bit
  115.         DMA1_Channel2->CCR |=    1<<7    ;        //存储器地址增量模式
  116.         DMA1_Channel2->CCR &= ~( 1<<6  ) ;        //不执行外设地址增量模式
  117.         DMA1_Channel2->CCR &= ~( 1<<5  ) ;        //执行循环操作
  118.         DMA1_Channel2->CCR &= ~( 1<<4  ) ;        //从外设读

  119.         DMA1_Channel2->CNDTR &= 0x0000   ;        //传输数量寄存器清零
  120.         DMA1_Channel2->CNDTR = SPI1_DMA_RXsize ;  //传输数量设置为buffersize个

  121.         DMA1_Channel2->CPAR = SPI1_DR_Addr ;      //设置外设地址,注意PSIZE
  122.         DMA1_Channel2->CMAR = (u32)SPI1_RX_Buff ; //设置DMA存储器地址,注意MSIZE
  123.         DMA1_Channel2->CCR |= 1 << 0 ;              //开启DMA通道2
  124.                          
  125. }
复制代码


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

385

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1023
金钱
1023
注册时间
2019-9-21
在线时间
269 小时
发表于 2021-11-25 18:40:49 | 显示全部楼层
回复

使用道具 举报

8

主题

42

帖子

0

精华

初级会员

Rank: 2

积分
161
金钱
161
注册时间
2014-1-7
在线时间
24 小时
 楼主| 发表于 2021-11-25 18:45:12 | 显示全部楼层
如果用SPI的发送DMA模式时,当DMA发送完15个字节后DMA1_Channel2->CNDTR =0,此时需要关闭DMA1_Channel2通道才能设置DMA1_Channel2->CNDTR,从而重新启动SPI的DMA发送数据,各位有没有好的办法在无CPU参与的情况下自动的设置DMA1_Channel2->CNDTR呢?
回复

使用道具 举报

8

主题

42

帖子

0

精华

初级会员

Rank: 2

积分
161
金钱
161
注册时间
2014-1-7
在线时间
24 小时
 楼主| 发表于 2021-11-25 18:50:18 | 显示全部楼层
gjs13030247996 发表于 2021-11-25 18:40
参考一下这个链接看一下:https://blog.csdn.net/darkghoul/article/details/108147840

非常感谢回复,这篇文章我也有看过,试了一下没有成功,关键是没有办法在DMA中重新设置DMA1_Channel2->CNDTR 的值,手册上说DMA的CNDTR寄存器只有在当前DMA通道关闭状态下才能设置
回复

使用道具 举报

8

主题

42

帖子

0

精华

初级会员

Rank: 2

积分
161
金钱
161
注册时间
2014-1-7
在线时间
24 小时
 楼主| 发表于 2021-11-25 18:58:30 | 显示全部楼层
d:\dma.jpg
回复

使用道具 举报

11

主题

2149

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4930
金钱
4930
注册时间
2015-1-10
在线时间
618 小时
发表于 2021-11-26 11:18:53 | 显示全部楼层
叶落浮华 发表于 2021-11-25 18:45
如果用SPI的发送DMA模式时,当DMA发送完15个字节后DMA1_Channel2->CNDTR =0,此时需要关闭DMA1_Channel2通 ...

你DMA不是开循环了嘛,正常就是触发一次发15个字节,不需要重设CNDTR,类似ADC+DMA,看看配置上是不是还有其他问题
回复

使用道具 举报

8

主题

42

帖子

0

精华

初级会员

Rank: 2

积分
161
金钱
161
注册时间
2014-1-7
在线时间
24 小时
 楼主| 发表于 2021-11-28 07:40:42 | 显示全部楼层
阿侑kevin 发表于 2021-11-26 11:18
你DMA不是开循环了嘛,正常就是触发一次发15个字节,不需要重设CNDTR,类似ADC+DMA,看看配置上是不是还 ...

非常感谢回复,我程序设置的是在TIM2->CC4捕获时触发的DMA,此时设置的数据长度虽然是15Byte但是每次触发DMA时只能发送1个数据而且每触发1次DMA发送的数据位在数组中的位置也是自动+1的;
我试过设置SPI自动发送触发的DMA,此时就能够1次发送15个数据,但是每次发送完15个数据要进入中断执行一下3个步骤:1、关闭当前SPI发送DMA通道;2、重新设置DMA1_Channel2->CNDTR =15;3、打开当前SPI发送DMA通道;
但是这样就不是我想要实现的在无CPU干预的情况下遇到下降沿SPI自动给发送15个字节的结果,是不是有别的更好的办法呢?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-13 22:54

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表