OpenEdv-开源电子网

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

定时器使用中的问题

[复制链接]

14

主题

101

帖子

2

精华

中级会员

Rank: 3Rank: 3

积分
493
金钱
493
注册时间
2011-9-6
在线时间
3 小时
发表于 2011-11-15 13:37:53 | 显示全部楼层 |阅读模式

大家好:
      我想实现的功能是串口以DMA方式接收数据,然后将数据以通过SPI写入FLASH中. 环境:MINI STM32开发板 发送数据:串口助手
      现在的问题:
      1 按照原子的例程写falsh没有问题,验证了SPI 和FLASH基本功能正常.
      2 串口以DMA双缓冲形式接收数据后,将数据写入FLASH中,这个也实现了. 在试验过程中会出现串口助手发送数据停止,DMA最后一次接收未完成,则最后接收的数据不能写入  FLASH中.
      3 为了改进2中的问题,引入了定时时钟.每次写FLASH完成后开启时钟开始5S钟计时,若5S中内DMA接收完成则将数据写入FLASH,然后设置定时器重新开始计时;若5S中内DMA接收未完成,则将DMA接收到的数据写入FLASH.(注: 串口助手没500ms发送100个数据,DMA接收数据长度为200)
      4 出现问题就在3中开启定时器后,DMA接收3次后会出现复位的现象,DMA接收也停止. 
     
      针对问题4 以为是中段优先级的设置问题,然后将DMA中段和定时器中段的优先级  组、子优先级设置相同,抢占优先级设置不同. 现象依旧存在,仔细看用户手册定时器设置没有问题。程序如下:
#include <stm32f10x_lib.h>
#include "sys.h"
#include "usart.h"  
#include "delay.h" 
#include "led.h"
#include "key.h"
#include "lcd.h"   
#include "flash.h"
#include "dma.h"  

#define SIZE 200
char ReceiveBuffer0[SIZE];  //DMA接收缓冲0
char ReceiveBuffer1[SIZE];  //DMA接收缓冲1
u16 count=0;
u16 len;
u8 Timeout=0;
int  Buff_OK=0;  //DMA已经有接受完数据
int  Free_Buff_No;  //DMA缓冲可以进行处理的数据

void Set_DMA_ISR(void);
void USART_Receive_DMA(void);
void Timer3_Init(u16 arr, u16 psc);


int main(void)
{  
 u16 i=0;    
   Stm32_Clock_Init(9);//系统时钟设置
 delay_init(72);  //延时初始化
 uart_init(72,9600); //串口1初始化  
 LED_Init();      //LED初始化
 KEY_Init();      //按键初始化
 LCD_Init();      //TFTLCD液晶初始化
 SPI_Flash_Init();   //SPI FLASH 初始化

  OINT_COLOR=RED;//设置字体为红色   
 LCD_ShowString(17,30,"MINISTM32 Flash Test!");  
 while(SPI_Flash_ReadID()!=FLASH_ID)//检测不到W25X16
 {   
  i=SPI_Flash_ReadID();
  printf("ID:%d",i);
  LCD_ShowString(60,110,"W25X16 Check Failed!");
  delay_ms(500);
  LCD_ShowString(60,110,"   Please Check!    ");
  delay_ms(500);
  LED0=!LED0;     //DS0闪烁
 }
 LCD_ShowString(60,110,"W25X16 Ready!");
 SPI_Flash_Erase_Chip(); //擦出整个FLASH
 USART_Receive_DMA(); //DMA开始第一次接收数据  
 while(1)
 {
  if(Buff_OK==1){
   Buff_OK=0;
   switch(Free_Buff_No){
    case 0: SPI_Flash_Write((u8*)ReceiveBuffer0,(SIZE*(count-1)),SIZE);
               break;
    case 1: SPI_Flash_Write((u8*)ReceiveBuffer1,(SIZE*(count-1)),SIZE); 
            break; 
    default: break;
   }
   Timer3_Init(50000,7199);
   TIM3->CR1|=0x01;    //使能定时器3 
  }
  if(Timeout==1){    //5秒钟内DMA接收没有完成
       TIM3->CR1|=0x0;    //停止定时器3
        len=SIZE-DMA1_Channel5->CNDTR;  //计算DMA接收数据长度
        if(Free_Buff_No==1)
               SPI_Flash_Write((u8*)ReceiveBuffer1,SIZE*count,len);
        else
              SPI_Flash_Write((u8*)ReceiveBuffer0,SIZE*count,len);
   count+=1;
   break;    //这句删除就可以实现了 但是还是有点小问题
  }    
 }
}

//DMA中断设置
void Set_DMA_ISR(void){
   DMA1_Channel5->CCR|=1<<1;  //允许TC中断 
   MY_NVIC_Init(0,3,DMA1_Channel5_IRQChannel,2); //中断优先级  抢占0,子优先级3,组2
}

//DMA1中断函数
void DMAChannel5_IRQHandler(void){
 if(DMA1->ISR&(1<<17)){      //DMA1通道5传输完成
      DMA1->IFCR|=1<<17;   //清除DMA1通道5传输完成标志
      count++;
      LCD_ShowNum(60,90,count,5,16);
     if(Free_Buff_No==0){
           MYDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)ReceiveBuffer0,SIZE);//DMA1通道5,外设为串口1,存储器为SendBuff,长度200.       
          Set_DMA_ISR();
          USART1->CR3=1<<6;            //使能串口1的DMA接收
          MYDMA_Enable(DMA1_Channel5); //开始一次DMA接收
         Free_Buff_No=1;
   }
  else{
      MYDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)ReceiveBuffer1,SIZE);//DMA1通道5,外设为串口1,存储器为SendBuff,长度200.       
      Set_DMA_ISR();
      USART1->CR3=1<<6;            //使能串口1的DMA接收
      MYDMA_Enable(DMA1_Channel5); //开始一次DMA接收
      Free_Buff_No=0;
  }
  Buff_OK=1;
 }
}

//USART1用DMA方式接收数据
void USART_Receive_DMA(void){
    Free_Buff_No=1;       
    MYDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)ReceiveBuffer0,SIZE);//DMA1通道5,外设为串口1,存储器为SendBuff,长度200.
    Set_DMA_ISR();     //开启中段
    USART1->CR3=1<<6;           //使能串口1的DMA接收      
    MYDMA_Enable(DMA1_Channel5);//开始一次DMA接收  
}


//定时器3中断服务程序 
void TIM3_IRQHandler(void)
{                 
    if(TIM3->SR&0X0001)//溢出中断
    {
       Timeout=1;                         
    }      
   TIM3->SR&=~(1<<0);//清除中断标志位     
}
//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void Timer3_Init(u16 arr, u16 psc)
{
    RCC->APB1ENR|=1<<1;//TIM3时钟使能   
    TIM3->ARR=arr;  //设定计数器自动重装值//刚好1ms   
    TIM3->SC=psc;  //预分频器7200,得到10Khz的计数时钟
    //这两个东东要同时设置才可以使用中断
    TIM3->DIER|=1<<0;   //允许更新中断    
    TIM3->DIER|=1<<6;   //允许触发中断
              
// TIM3->CR1|=0x01;    //使能定时器3
   MY_NVIC_Init(1,3,TIM3_IRQChannel,2);//抢占1,子优先级3,组2       
}

 

大家帮忙看下是不是有什么地方设置不对,谢谢啦!

 

 

 

 

 

 


     

事无巨细 循序渐进
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2011-11-15 13:46:03 | 显示全部楼层
你主函数最后那个break,是不是会跳出主循环了?
这样你的main函数就执行over了...
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

14

主题

101

帖子

2

精华

中级会员

Rank: 3Rank: 3

积分
493
金钱
493
注册时间
2011-9-6
在线时间
3 小时
 楼主| 发表于 2011-11-15 14:07:47 | 显示全部楼层
回复【2楼】正点原子:
---------------------------------
谢谢原子哥指点,但是还是有点问题 
if(Timeout==1){    //5秒钟内DMA接收没有完成
       TIM3->CR1|=0x0;    //停止定时器3
        len=SIZE-DMA1_Channel5->CNDTR;  //计算DMA接收数据长度
        if(Free_Buff_No==1)
               SPI_Flash_Write((u8*)ReceiveBuffer1,SIZE*count,len);
        else
              SPI_Flash_Write((u8*)ReceiveBuffer0,SIZE*count,len);
   count+=1;    //这一句的取舍对于现实DMA接收次数有影响
  }    
 (1) 去掉count+=1; count现实从1开始持续向上增加
 (2) 不去掉 count+=1;  会突然出现count向上加2的现象 很明显进入定时器进入了中断 也就说明每次写FLASH后计数器应该没有被重置,没有重新开始计数.
   不知道我的分析是否正确? 望指点...

事无巨细 循序渐进
回复 支持 反对

使用道具 举报

14

主题

101

帖子

2

精华

中级会员

Rank: 3Rank: 3

积分
493
金钱
493
注册时间
2011-9-6
在线时间
3 小时
 楼主| 发表于 2011-11-15 17:42:27 | 显示全部楼层
if(Buff_OK==1){
   Buff_OK=0;
   switch(Free_Buff_No){
    case 0: SPI_Flash_Write((u8*)ReceiveBuffer0,(SIZE*(count-1)),SIZE);
               break;
    case 1: SPI_Flash_Write((u8*)ReceiveBuffer1,(SIZE*(count-1)),SIZE); 
            break; 
    default: break;
   }
   Timer3_Init(50000,7199);
   TIM3->CR1|=0x01;    //使能定时器3  
  }
   每写入一次FLASH,预分频的值和重装载的值都被更新,但是TIMx_CNT中的计数值不会被清零,所以就会出现2楼中的现象:不去掉 count+=1;  会突然出现count向上加2 呈现规律性  
  程序中使用TIM3->CNT=0;也不可以将TIMx_CNT中的计数值清零. 请问该如何做才可以实现TIMx_CNT中的计数值清零? 谢谢!
事无巨细 循序渐进
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2011-11-15 22:51:36 | 显示全部楼层
回复【4楼】葱花鱼:
---------------------------------
软件仿真吧.
这种软件问题,得慢慢测.跟踪代码.
此时你会发现:有个JTAG真好.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

14

主题

101

帖子

2

精华

中级会员

Rank: 3Rank: 3

积分
493
金钱
493
注册时间
2011-9-6
在线时间
3 小时
 楼主| 发表于 2011-11-21 14:13:28 | 显示全部楼层
原子哥:
      找到问题所在了,用我自己的DMA接收串口数据后写入FLASH的程序和开发板定时器例程都存在这样一个问题:定时器第一次开启后就产生一次更新事件.
我利用<<例说STM32>>第十二章定时器中断实验进行JTAG单步调试,发现定时器第一次开启后就产生一次更新事件,仿真结果如下:
(1)定时器未开启: CEN=0 UIF=0


(2)定时器第一次开启: CEN=1  UIF=1




(3)UIF说明




我的写FLASH程序在第一次DMA接收完成后产生中断,然后开始第二个缓冲区的接收,将第一个缓冲区内容写入FLASH,同时开启TIM3进行5S计时,若5S内DMA接收未完成则将DMA已接受数据写入FLASH中. 每次都是count(产生一次DMA中断count 增加1)直接从1到3,也就是第一次开启定时器后进入了一次中断使Timeout=1.程序如下:
void Receive_WriteFlash(void){ 
 while(1){
  if(Buff_OK==1){
   Buff_OK=0;
   switch(Free_Buff_No){
    case 0: SPI_Flash_Write((u8*)ReceiveBuffer0,(SIZE*(count-1)),SIZE);
      break;
    case 1: SPI_Flash_Write((u8*)ReceiveBuffer1,(SIZE*(count-1)),SIZE); 
            break; 
    default: break;
   }
   TIM3->CR1&=0xfe;    //停止定时器3
   TIM3->CNT=0;     //计算器清零
   Timer3_Init(50000,7199);//定时器初始化 定时5S
   TIM3->CR1|=0x01;    //使能定时器3
  }
  if(Timeout==1){    //5秒钟内DMA接收没有完成  定时器中断使Timeout=1
   TIM3->CR1&=0xfe;    //停止定时器3
   Timeout=0;
   len=SIZE-DMA1_Channel5->CNDTR;  //计算DMA接收数据长度
   if(Free_Buff_No==1)
    SPI_Flash_Write((u8*)ReceiveBuffer1,SIZE*count,len);
   else
    SPI_Flash_Write((u8*)ReceiveBuffer0,SIZE*count,len);
    count+=1;
   LCD_ShowNum(60,90,count,5,16);
   break;      //DMA停止接收数据, FLASH写入完成 跳出整个程序
  }
 }  
}
不知道我的分析是否正确,为什么第一次开启定时器后就产生一次更新事件使UIF=1还没有找到原因,希望大家帮忙看下,有没好的建议,谢谢!

事无巨细 循序渐进
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2011-11-21 15:29:44 | 显示全部楼层
回复【6楼】葱花鱼:
---------------------------------
第一次开启就进入很经常的问题,我也还没怎么研究。不过你可以试试在开启之前把中断标志位都给清除一遍,再试试是否会进入?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

14

主题

101

帖子

2

精华

中级会员

Rank: 3Rank: 3

积分
493
金钱
493
注册时间
2011-9-6
在线时间
3 小时
 楼主| 发表于 2011-11-21 15:52:04 | 显示全部楼层
回复【7楼】正点原子:
---------------------------------
(1)我将判断条件修改为 if((Timeout==1) && (count>NUM) ) ; count 计算大于NUM后再次进入这个循环,验证了每次时钟开启后产生了一次更新事件将UIF置位. 
(2)在时钟初始化函数中增加清除中断标志位语句 ,开启时钟后仍然产生更新事件.

不解中.......
事无巨细 循序渐进
回复 支持 反对

使用道具 举报

14

主题

75

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
248
金钱
248
注册时间
2011-10-16
在线时间
31 小时
发表于 2012-3-1 21:54:27 | 显示全部楼层
             看看那个初始化的库函数就知道啦!认真读手册,URS位设置后,一初始化就会产生更新中断的 ,如果不设置那个标志位就只会产生更新事件,不会产生更新中断,哈哈,我研究了一晚的英文Reference mannual 才弄明白的,中文的不行,说的不清不楚的,我一开始也被这个问题拖了一个多星期,希望别人不要像我走弯路了
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2012-3-1 22:19:52 | 显示全部楼层
谢谢分享.学习了
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

17

主题

168

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
260
金钱
260
注册时间
2014-4-12
在线时间
0 小时
发表于 2014-10-20 15:56:20 | 显示全部楼层
以后用到啥都得对照数据书册自己捣鼓了。
规格严格,功夫到家
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-30 09:03

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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