打印
[菜农助学交流]

第四批 笔记四—PWM初始化程序完全解析

[复制链接]
4589|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zxcscm|  楼主 | 2012-2-7 13:25 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 zxcscm 于 2012-2-7 13:35 编辑

园地里有很多关于PWM的例子,但是只看这些例子只能知其然不知所以然。索性逐字逐句地分析每条语句,更能深入的理解pwm的配置过程,并可举一反三地理解其他外设的配置
要具备的C语言基础知识:宏定义definetypedef 结构体,结构体指针,位域

先看整个程序工程:https://bbs.21ic.com/viewthread.php?tid=271140&highlight=pwm

(一)、DrvPWM_SelectClockSource(DRVPWM_TIMER0,DRVPWM_HCLK);
//选择系统时钟作为PWM的时钟

该函数传递了两个参数:DRVPWM_TIMER0,DRVPWM_HCLK

利用keil 的便捷查看:


找到




知道了传递的参数是0x002,接下来看看函数是怎么操作的
同样在函数名上点右键:




打开该函数:

void DrvPWM_SelectClockSource(uint8_t u8Timer, uint8_t
u8ClockSourceSelector)

{

       switch (u8Timer & 0x07)

       {

              case DRVPWM_TIMER0:

              case DRVPWM_TIMER1:

                     SYSCLK->CLKSEL1.PWM01_S
= u8ClockSourceSelector;                           

                     break;

              case DRVPWM_TIMER2:

              case DRVPWM_TIMER3:

                     SYSCLK->CLKSEL1.PWM23_S
= u8ClockSourceSelector;

                     break;            

              case DRVPWM_TIMER4:

              case DRVPWM_TIMER5:

                     SYSCLK->CLKSEL2.PWM45_S
= u8ClockSourceSelector;                           

                     break;

              case DRVPWM_TIMER6:

              case DRVPWM_TIMER7:

                     SYSCLK->CLKSEL2.PWM67_S
= u8ClockSourceSelector;

                     break;     

       }

}


由前面传递的参数0x002,可知道函数执行
SYSCLK->CLKSEL1.PWM01_S = u8ClockSourceSelector;      
关键的SYSCLK->CLKSEL1.PWM01_S卡住了,主要因为对结构体的理解不够,只是在以前看书时略知一二,正好趁此机会深入了解
没有现成的书可参考,在网上找到该贴https://bbs.21ic.com/icview-268846-1-1.html,给了我很大的提示

首先SYSCLK是什么
#define SYSCLK             ((SYSCLK_T *) SYSCLK_BASE)
这里定义为SYSCLK_T类型的指针,其值为SYSCLK_BASE

1. SYSCLK_T定义

typedef struct

{

    SYSCLK_PWRCON_T    PWRCON;

    SYSCLK_AHBCLK_T    AHBCLK;

    SYSCLK_APBCLK_T    APBCLK;

    SYSCLK_CLKSTATUS_T CLKSTATUS;

    SYSCLK_CLKSEL0_T   CLKSEL0;

    SYSCLK_CLKSEL1_T   CLKSEL1;

    SYSCLK_CLKDIV_T    CLKDIV;

    SYSCLK_CLKSEL2_T   CLKSEL2;

       SYSCLK_PLLCON_T    PLLCON;

       SYSCLK_FRQDIV_T    FRQDIV;

   

} SYSCLK_T;

SYSCLK_T等同于一个结构体类型,可用于定义结构体变量。

2. SYSCLK_BASE的定义
#define SYSCLK_BASE          (AHB_BASE       + 0x00200)
#define AHB_BASE            ((     uint32_t)0x50000000)
所以,SYSCLK_BASE代表的是物理地址0x5000_0200,由NUC1XX参考手册(5.2.1 P91)得知该地址是时钟控制寄存器的首地址

综上:SYSCLK代表指向地址0x5000_0200SYSCLK_T结构体指针。

其次:SYSCLK->CLKSEL1.PWM01_S
1.指向SYSCLK_T成员CLKSEL1的地址,该偏移地址为0x14
下面看一下该地址是怎么得来的
先要了解下M0的存储结构,从手册可看到M0的每个寄存器为32位(bit),即占4字节(byte
在结构体SYSCLK_TCLKSEL1被定义为SYSCLK_CLKSEL1_T
2.SYSCLK_CLKSEL1_T又是一个结构体类型,定义如下



typedef struct

{

    __IO uint32_t  WDT_S:2;

    __IO uint32_t  ADC_S:2;

    __I  uint32_t
RESERVE1:4;

    __IO uint32_t  TMR0_S:3;

    __I  uint32_t
RESERVE2:1;

    __IO uint32_t  TMR1_S:3;

    __I  uint32_t
RESERVE3:1;

    __IO uint32_t  TMR2_S:3;

    __I  uint32_t
RESERVE4:1;

    __IO uint32_t  TMR3_S:3;

    __I  uint32_t
RESERVE5:1;

    __IO uint32_t  UART_S:2;

    __IO uint32_t  CAN_S:2;

    __IO uint32_t  PWM01_S:2;

    __IO uint32_t  PWM23_S:2;

} SYSCLK_CLKSEL1_T;

可以看到,该结构体是有别于其他的结构体类型,里面的成员是位域的形式,这就说明SYSCLK_CLKSEL1_T32位的结构体,其成员则指具体的某一位。
所以SYSCLK_T的每个成员占32位即4字节,从而可将其各个偏移地址列出来
typedef struct
{
    SYSCLK_PWRCON_T    PWRCON;   0x00
    SYSCLK_AHBCLK_T    AHBCLK;    0x04
    SYSCLK_APBCLK_T    APBCLK;     0x08
    SYSCLK_CLKSTATUS_T CLKSTATUS;   0x0C
    SYSCLK_CLKSEL0_T   CLKSEL0;     0x10
    SYSCLK_CLKSEL1_T   CLKSEL1;     0x14
    SYSCLK_CLKDIV_T    CLKDIV;       0x18
    SYSCLK_CLKSEL2_T   CLKSEL2;     0x1C
       SYSCLK_PLLCON_T    PLLCON;       0x20
       SYSCLK_FRQDIV_T    FRQDIV;        0x24
   
} SYSCLK_T;

到这里即可算出SYSCLK->CLKSEL1.PWM01_S的具体地址了
0x5000_0200+0x042829位,这两位就是 时钟源选择控制寄存器1CLKSEL1)中的 PWM1 PWM0的时钟源选择.

所以SYSCLK->CLKSEL1.PWM01_S= u8ClockSourceSelector;向时钟源选择控制寄存器1CLKSEL1)的第2928位写入二进制数10,选择HCLK作为PWM0的时钟源




(二)、DrvGPIO_InitFunction(E_FUNC_PWM01);     
//  设置GPIO口的PWM01功能

该函数的原型就不列了,只看函数会用到的语句,如下图



首先来看传递的具体参数&SYS->GPAMFP,有了前面的了解现在能猜到,这个是某个寄存器的地址
#define SYS                 ((GCR_T*) GCR_BASE)
1SYSGCR_T结构体类型指针,指向首地址GCR_BASE



得出GCR_BASE=0x50000000
2SYS->GPAMFP
找出GPAMFP的偏移地址
typedef struct
{
    GCR_PDID_T      PDID;          0x00
    GCR_RSTSRC_T    RSTSRC;       0x04
    GCR_IPRSTC1_T   IPRSTC1;        0x08
    GCR_IPRSTC2_T   IPRSTC2;        0x0C
       GCR_CPR_T       CPR;            0x10
       uint32_t        RESERVE0;         0x14
    GCR_BODCR_T     BODCR;        0x18
    GCR_TEMPCR_T    TEMPCR;       0x1C
       uint32_t        RESERVE1;          0x20
       GCR_PORCR_T           PORCR;        0x24
       uint32_t        RESERVE2[2];        0x28 注意:此处是个数组
    GCR_GPAMFP_T    GPAMFP;        0x30
    GCR_GPBMFP_T    GPBMFP;        0x34
    GCR_GPCMFP_T    GPCMFP;        0x38
    GCR_GPDMFP_T    GPDMFP;        0x3C
    GCR_GPEMFP_T    GPEMFP;        0x40
    uint32_t        RESERVE3[3];        0x44
       GCR_ALTMFP_T    ALTMFP;        0x50
    uint32_t        RESERVE4[43];       0x54
    GCR_REGLOCK_T   REGLOCK;    0x100
       uint32_t        RESERVE5[3];        0x104
       GCR_RCADJ_T            RCADJ;        0x110
} GCR_T;
因此:&SYS->GPAMFP所指向的地址是:0x50000000+0x30  
由手册知这个地址是:多功能GPIOA控制寄存器((GPA_MFP)

再回过来看看outpw(&SYS->GPAMFP,inpw(&SYS->GPAMFP) | (0x3<<12))的定义




大意是将value赋给port所指向的地址,因此outpw(&SYS->GPAMFP,inpw(&SYS->GPAMFP) | (0x3<<12))就是将&SYS->GPAMFP的第1312位置1
由手册(P104)知是将端口PA13PA12设置为PWM

函数DrvGPIO_InitFunction();内的其他语句也是实现类似的给某个寄存器赋值,设置端口功能


(三)、DrvPWM_Open();   
//使能PWM时钟并复位

定义:
/*Description: Enable PWM engine clock and reset PWM                         */
/*                                                                                                         */
/*---------------------------------------------------------------------------------------------------------*/
void DrvPWM_Open(void)
{
       outp32(&SYSCLK->APBCLK,inp32(&SYSCLK->APBCLK) | 0x00F00000);//第一句
       outp32(&SYS->IPRSTC2,inp32(&SYS->IPRSTC2) | 0x00300000);         //第二句
       outp32(&SYS->IPRSTC2,inp32(&SYS->IPRSTC2) & ~0x00300000);       //第三句
}
简单的分析每句的功能
第一句、找出&SYSCLK->APBCLK对应地址,
在前面已知道SYSCLK代表指向地址0x5000_0200SYSCLK_T结构体指针,其成员APBCLK的偏移地址是0x08.
对照手册P91找到CLK_BA  
P161找到APBCLK  确定APBCLK的地址:0x5000_0208   
P166  因此该句实现:使能所有pwm时钟
第二句、&SYS->IPRSTC2
SYS是指向地址0x5000_0000GCR_T结构体类型指针,其成员IPRSTC2的偏移地址是0x0C
对照手册P98 确定地址和功能:向地址0x5000_000C2122位写入1,复位PWM模块
第三句、向地址0x5000_000C2122位写入0 PWM模块正常工作

分析完这三个函数,后面的程序也就无需分析也能知道函数内部是具体怎么操作硬件的了。

参考:
1PWM例程https://bbs.21ic.com/viewthread.php?tid=271140&highlight=pwm
2】新塘官方BSP例程:NUC100SeriesBSP_v1.05.002
3Timer应用之理解C语言位域:https://bbs.21ic.com/icview-268846-1-1.html
4NUC1XX参考手册




完整文件

笔记四—PWM初始化程序完全解.pdf

70.76 KB

相关帖子

沙发
xyz549040622| | 2012-2-7 13:54 | 只看该作者
很详细,拜读了,顶

使用特权

评论回复
板凳
raja21| | 2012-7-3 17:08 | 只看该作者
很详细,谢谢楼主!

使用特权

评论回复
地板
ljp98| | 2012-7-11 08:56 | 只看该作者
楼主真细心。顶楼主。

使用特权

评论回复
5
lee9888| | 2012-7-11 15:29 | 只看该作者
我也是用示波器边调边改才出来想要的效果

使用特权

评论回复
6
luabc| | 2012-7-21 17:12 | 只看该作者
似乎看不懂

使用特权

评论回复
7
nomorehest| | 2013-2-24 01:06 | 只看该作者
楼主太用心了, 感谢了.

使用特权

评论回复
8
a437916817| | 2013-4-9 15:54 | 只看该作者
多谢分享

使用特权

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

本版积分规则

5

主题

628

帖子

1

粉丝