打印
[新手园地]

HOT大叔NUC120助学板第八贴----DrvSYS_Delay()浅析

[复制链接]
6657|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 Swallow_0322 于 2011-4-15 14:22 编辑

      前天晚上有人在大群中讨论关于DrvSYS_Delay()的问题,我未加仔细研究便给出答复,最大时钟频率50MHz加上是24位的滴答定时器,所以其入口参数的最大值为335000,今天抽空调试了一下与我的作答有些出入,对于我未经确认没有十足把握就去误导网友深表歉意!下面是我对这个函数的理解,本人菜鸟,理解浅薄,如果错误望不吝指教,俺勇于接受大家抛过来的砖头,能把我从迷糊中砸醒也是好事,(*^__^*) 嘻嘻……


关于对函数DrvSYS_Delay()的理解:
① 本函数是使用系统定时器 (SysTick) ,也就是常说的滴答时钟或者系统脉搏时钟,SysTick 提供一种简单,24位计数器,可灵活控制;

② 通过获取SysTick 时钟源选择位(SYSCLK->CLKSEL0.STCLK_S)可知其为0x07即系统滴答的时钟源为内部 22MHz 振荡器 1/2分频;

③ 通过串口调试SystemCoreClock的值为22118400Hz



对函数DrvSYS_Delay()浅析:

第一句调整为:SysTick->LOAD = (us * (SystemCoreClock/2 / 10000)) / 100; 因为(SystemCoreClock/2)才是真正的系统滴答时钟,该句的作用为赋值SysTick 重新加载值寄存器(SYST_RVR);

第二句SysTick->VAL  = (0x00); 该句作用为软件清寄存器为0,其实该值为任何值均可;

第三句SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; 该句作用为使能系统滴答并将CLKSRC11 : 如果没有外部时钟时,可用内核时钟作SysTick, 该位被读为1 ,不能写.0 : 时钟源为外部参考时钟 ),TICKINT0(向下计数到0不会引起SysTick异常而挂起. 软件设置COUNTFLAG 来确定是否已经发生计数到0);

第四句:while((SysTick->CTRL & (1 << 16)) == 0);计数由10时,COUNTFLAG 置位,即等待设置的延时结束。


The Max value 我认为应该为:(1/(22118400/2))*224 *106= 1,521,114μs;


测试源文件:

/**************************************************
** 文件名称:NUC120_HOT_DrvSYS_Delay_TEST.c
** 文件说明:NUC120助学板练习程序
** 创建日期:2011-04-15
** 修改日期:
** 备    注:DrvSYS_Delay()测试
**************************************************/
#include <stdio.h>
#include "NUC1xx.h"
#include "Driver\DrvGPIO.h"
#include "Driver\DrvSYS.h"
#include "Driver\DrvUART.h"

#define  Run_Led 2    //2----LED1  3----LED2   4----LED3  5----LED4
#define  Delay_Led 5    //延时函数波形测试辅助 2----LED1  3----LED2   4----LED3  5----LED4
uint32_t Delay_Val = 1000;   //DrvSYS_Delay()延时设置值默认为1000μs
/***************
**  函数声明  **
***************/
void Init_System (void);
void Init_Uart (void);
void UART_INT_HANDLE(uint32_t u32IntStatus);

/*****************************
** Name:      UART_INT_HANDLE
** Function:  UART Callback function
** Input:     u32IntStatus
** OutPut:    None
** Data:      2011-03-17
** Note:      
****************************/
void UART_INT_HANDLE(uint32_t u32IntStatus)
{
  uint8_t bInChar[1]={0xFF};
if(u32IntStatus & DRVUART_RDAINT)
{
  /* Get all the input characters */
  while(UART0->ISR.RDA_IF==1)
  {
   /* Get the character from UART Buffer */
   DrvUART_Read(UART_PORT0,bInChar,1);
   switch (bInChar[0])
   {
    case '1':
     Delay_Val = 1000;
     break;
    case '2':
     Delay_Val = 1000000;
     break;
    case '3':
     Delay_Val = 300000;
     break;
    default:
     printf("请确认输入指令是否正确!\n");
     break;
   }
   
  }
}
}
/*****************************
** Name:      Init_System
** Function:  系统初始化函数
** Input:      None
** OutPut:     None
** Data:       2011-03-17
** Note:      
****************************/
void Init_System(void)
{
/* Unlock the locked registers before access */
    UNLOCKREG(x); //寄存器锁定键地址寄存器(RegLockAddr) :有些系统控制寄存器需要被保护起来,以防止误操作而影响芯片运行,
        //这些寄存器在上电复位到用户解锁定之前是锁定的。用户可以连续依次写入“59h”, “16h” “88h”到0x5000_0100解锁定.
/* Enable the 12MHz oscillator oscillation */
DrvSYS_SetOscCtrl(E_SYS_XTL12M, 1);        //SYSCLK->WRCON.XTL12M_EN = 1;
/* Waiting for 12M Xtal stable */
//while (DrvSYS_GetChipClockSourceStatus(E_SYS_XTL12M) != 1);  //SYSCLK->CLKSTATUS.XTL12M_STB
/*eClkSrc  - [in]  E_SYS_XTL12M / E_SYS_XTL32K / E_SYS_OSC22M / E_SYS_OSC10K / E_SYS_PLL    */
// Note: Only some of NuMicro NUC100 Series support this function.
DrvSYS_Delay(5000);
LOCKREG(x);
//向“0x5000_0100”写入任何值,就可以重锁保护寄存器
}
/*****************************
** Name:      Init_Uart
** Function:  UART初始化函数
** Input:      None
** OutPut:     None
** Data:       2011-03-17
** Note:      
****************************/
void Init_Uart(void)
{
STR_UART_T param;
/*
声明 UART设置的结构体 位于DRVUART.H
结构体如下
typedef struct DRVUART_STRUCT
{
  uint32_t            u32BaudRate;   
  E_DATABITS_SETTINS  u8cDataBits;   
  E_STOPBITS_SETTINS  u8cStopBits;   
  E_PARITY_SETTINS  u8cParity;     
  E_FIFO_SETTINGS     u8cRxTriggerLevel;   
  uint8_t             u8TimeOut ;     
      }STR_UART_T;
*/
DrvSYS_SelectIPClockSource(E_SYS_UART_CLKSRC,0);      //使能UART时钟
//SYSCLK->CLKSEL1.UART_S = 0;  //UART时钟源选择. 00 =外部12MHz 晶振 01 = PLL 1x =内部 22MHz 振荡器
DrvGPIO_InitFunction(E_FUNC_UART0);          //GPB_MFP0-1-2-3置位 GPIO使能UART功能
//outpw(&SYS->GPBMFP, inpw(&SYS->GPBMFP) | (0xF<<0));
param.u32BaudRate        = 115200;       //  波特率
param.u8cDataBits        = DRVUART_DATABITS_8;    //  数据位
param.u8cStopBits        = DRVUART_STOPBITS_1;    //  停止位
param.u8cParity          = DRVUART_PARITY_NONE;    //  校验位
param.u8cRxTriggerLevel  = DRVUART_FIFO_1BYTES;    //  FIFO存储深度 1 字节
param.u8TimeOut          = 0;        //  FIFO超时设定
/* Set UART Configuration */
  if(DrvUART_Open(UART_PORT0,&param) != E_SUCCESS)   //  串口开启、结构体整体赋值
  printf("UART0 open failed\n");      
/* u32Port -[in] UART Channel:  UART_PORT0 / UART_PORT1 /UART_PORT2 */
/* sParam  -[in] the struct parameter to configure UART    */   
/* Enable Interrupt and install the call back function */
DrvUART_EnableInt(UART_PORT0, DRVUART_RDAINT,UART_INT_HANDLE);
/*u32Port     -[in] UART Channel:  UART_PORT0 / UART_PORT1 / UART_PORT2                   */
/*u32InterruptFlag -[in] DRVUART_LININT/DRVUART_WAKEUPINT/DRVUART_BUFERRINT/DRVUART_RLSINT     */
/*          DRVUART_MOSINT/DRVUART_THREINT/DRVUART_RDAINT/DRVUART_TOUTINT   */
/*pfncallback      -[in] A function pointer for callback function                              */
}
int main (void)
{
uint8_t test = 250;

Init_System();

Init_Uart();
DrvGPIO_Open(E_GPA,Run_Led, E_IO_OUTPUT);      //程序运行指示
DrvGPIO_ClrBit(E_GPA,Run_Led);
DrvGPIO_Open(E_GPA,5, E_IO_OUTPUT);        //延时函数波形测试
printf("\n");
printf("/*==========================\n");
printf("======菜农 %d 助学计划======\n",test);
printf("========NUC120助学板========\n");
printf("======程序参考新唐BSP=======\n");
printf("=======2011年04月15日=======\n");
printf("===DrvSYS_Delay()函数实验===\n");
printf("HCLK: %dHZ\n ",DrvSYS_GetHCLKFreq());
printf("SystemCoreClock: %dHZ\n",SystemCoreClock);
printf("SelectSysTickSource: 0x0%x\n",SYSCLK->CLKSEL0.STCLK_S);
printf("发送'1'延时值为1000,'2'延时值为1000000,'3'延时值为300000.\n");
printf("==========================*/\n");
    while(1)
    {
     DrvGPIO_ClrBit(E_GPA,Delay_Led);
  DrvSYS_Delay(Delay_Val);
  DrvGPIO_SetBit(E_GPA,Delay_Led);
  DrvSYS_Delay(Delay_Val);
  
    }
}


工程结构:




串口调试截图:



延时值设置为1000μs测试波形图:



延时值设置为300000μs测试波形图:



延时值设置为1000000μs测试波形图:




程序很简单,就不上传工程文件了,浪费二姨空间!

相关帖子

沙发
6163725| | 2011-4-15 14:20 | 只看该作者
力挺!

使用特权

评论回复
板凳
weshiluwei6| | 2011-4-15 19:34 | 只看该作者
大哥 又见面啦 力挺 支持 板凳

使用特权

评论回复
地板
Swallow_0322|  楼主 | 2011-4-16 08:34 | 只看该作者
3# weshiluwei6

3Q!:handshake

使用特权

评论回复
5
weshiluwei6| | 2011-4-16 13:06 | 只看该作者
4# Swallow_0322
您做事好认真啊 支持啊

使用特权

评论回复
6
hotpower| | 2011-4-16 18:23 | 只看该作者
很好,但俺喜欢用systick做节拍,做裸奔的心脏。

使用特权

评论回复
7
Swallow_0322|  楼主 | 2011-4-17 22:02 | 只看该作者
5# weshiluwei6

呵呵 谢谢 :P

使用特权

评论回复
8
Swallow_0322|  楼主 | 2011-4-17 22:03 | 只看该作者
6# hotpower

恩 谢谢大叔百忙之中指点 :handshake

使用特权

评论回复
9
tlb| | 2011-4-26 07:34 | 只看该作者
很好的经验

使用特权

评论回复
10
dong_abc| | 2011-4-26 12:14 | 只看该作者
本帖最后由 dong_abc 于 2011-4-26 12:15 编辑

裸奔用systick作系统节拍最好不过了。我还是用的一个定时器。

使用特权

评论回复
11
lixupengarm| | 2011-5-23 17:58 | 只看该作者
mark!!

使用特权

评论回复
12
侠盗李二| | 2011-8-5 00:20 | 只看该作者
我想问一下,systick用的是内核22.1184Mhz,但是初始化的时候不用使能这个22.1184Mhz的吗?
或者是,系统上电以后,这个内核时钟本身已经是使能的呢?

使用特权

评论回复
13
nomorehest| | 2012-1-29 15:03 | 只看该作者
我感觉用DrvSYS_Delay延时很不准啊, 我无论采用外接晶振还是内部的22M晶振都不行.
从你的图上也能看每种延时都有误差.
有其他更精确的延时方法吗?

使用特权

评论回复
14
nomorehest| | 2012-1-30 12:15 | 只看该作者
今天仔细实验了楼主的程序. 我发现当我把
DrvSYS_SetOscCtrl(E_SYS_XTL12M, 1);  改成DrvSYS_SetOscCtrl(E_SYS_OSC22M, 1);时, 就是启用内置的22MHZ振荡器, 发现定时时间错误非常大.1S的定时可以差半秒. 我不得不重新研究了这个段代码.
我发现无论你设定成外接晶振或者内部振荡器, 都没有对SYSTICK时钟进行设置, 因此定时就不正确, 而且CyclesPerUs是通过函数SystemCoreClockUpdate得到的.但是对这个函数, 如果我的HCLK和SYSTICK都用22MHZ(systick要2分频),发现程序算出来的CyclesPerU是22,而通过公示算出来是11.
而且我不知道CyclesPerUs = (SystemCoreClock + 500000) / 1000000;        这个是怎么得出来的.
有高手可以帮忙看一下吗?

使用特权

评论回复
15
Swallow_0322|  楼主 | 2012-1-30 13:53 | 只看该作者
本帖最后由 Swallow_0322 于 2012-1-30 14:01 编辑
今天仔细实验了楼主的程序. 我发现当我把
DrvSYS_SetOscCtrl(E_SYS_XTL12M, 1);  改成DrvSYS_SetOscCtrl(E_SYS_OSC22M, 1);时, 就是启用内置的22MHZ振荡器, 发现定时时间错误非常大.1S的定时可以差半秒. 我不得不重 ...
nomorehest 发表于 2012-1-30 12:15


您好!不好意思,这么才回复!感谢您的关注!

您仔细试验了我的程序,但阅读我前面的内容了吗?
第一句调整为:SysTick->LOAD = (us * (SystemCoreClock/2 / 10000)) / 100; 因为(SystemCoreClock/2)才是真正的系统滴答时钟,该句的作用为赋值SysTick 重新加载值寄存器(SYST_RVR);”
CyclesPerUs = (SystemCoreClock + 500000) / 1000000
                    =SystemCoreClock /1000000+0.5
单位由s调整为us且四舍五入了!
你所说的使能外部12M晶振还是内部22M的振荡器你要看你的SysTick的时钟源是选择哪个?在我的程序中我没有去主动设置选择哪个时钟源,我只是确认了一下默认选择的时钟源是哪个?

以上内容仅为个人理解,如有失误之处请谅解!

使用特权

评论回复
16
Swallow_0322|  楼主 | 2012-1-30 14:05 | 只看该作者
我想问一下,systick用的是内核22.1184Mhz,但是初始化的时候不用使能这个22.1184Mhz的吗?
或者是,系统上电以后,这个内核时钟本身已经是使能的呢?
侠盗李二 发表于 2011-8-5 00:20


不好意思,这么晚才回复您!

SYST_CSR复位后的值为0x0000_0004

使用特权

评论回复
17
nomorehest| | 2012-1-30 16:08 | 只看该作者
您好!不好意思,这么才回复!感谢您的关注!

您仔细试验了我的程序,但阅读我前面的内容了吗?
“第一句调整为:SysTick->LOAD = (us * (SystemCoreClock/2 / 10000)) / 100; 因为(SystemCoreClock/2)才是真正 ...
Swallow_0322 发表于 2012-1-30 13:53


嗨,你好,感谢你的答复.
我先告诉你的我所做的修改:
1. 我使用内部的22MHZ振荡器, 即HCLK选择内部22MHZ的,SYSTICK也选择内部22MHZ/2的.
我在那的Init_System加了下面两句,并将你之前那句禁用了:
//DrvSYS_SetOscCtrl(E_SYS_XTL12M, 1);        //SYSCLK->WRCON.XTL12M_EN = 1;
DrvSYS_SetOscCtrl(E_SYS_OSC22M, 1);
DrvSYS_SelectSysTickSource(7);    //0: External 12M clock 2:External 12M clock/2 3: HCLK/2 4~7:Internal 22M clock/2
DrvSYS_SelectHCLKSource(7);   //0: External 12M clock 2:PLL clock 3: Internal 10K clock  7:Internal 22M clock
2. 按照M0516的芯片手册, SYSTICK的时钟应该是22MHZ的一半. 但是对于DrvSYS_Delay()函数, 我发现它仍然使用的是自己默认的时钟,即12MHZ, 如下:
SysTick->LOAD = us * CyclesPerUs;  这是DrvSYS_Delay()函数中的.
而uint32_t CyclesPerUs      = (__HSI / 1000000);   这里
#define __XTAL      (12000000UL)
#define __IRC22M    (22118400UL)
#define __IRC10K    (10000UL)
#define __HSI       (__XTAL)
因此可以看到__HSI的值是12M.
如果是这样,我觉得好像明显不对, 如果要使用内部的22M,必须要对DrvSYS_Delay()这个函数进行改造, 这样理解对吧?
3. 我通过使用你的提供的函数My_DrvSYS_Delay(), 将 SysTick->LOAD = us * (SystemCoreClock/2/10000) / 100; 这样以后我实验了一下, 发现程序跑1分钟后能芯片这边能快10秒, 额滴神那. 不知道是不是我哪里出错了?
感谢你不吝指教.

使用特权

评论回复
18
changmiao| | 2012-4-9 20:47 | 只看该作者
一步步追踪回去,其实会发现人家计算的公式就是22M的,用宏定义的,那还能改成12M?

使用特权

评论回复
19
changmiao| | 2012-4-9 20:47 | 只看该作者
一步步追踪回去,其实会发现人家计算的公式就是22M的,用宏定义的,那还能改成12M?

使用特权

评论回复
20
changmiao| | 2012-4-9 20:48 | 只看该作者
一步步追踪回去,其实会发现人家计算的公式就是22M的,用宏定义的,那还能改成12M?

使用特权

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

本版积分规则

121

主题

1393

帖子

4

粉丝