您的位置:首页 > 其它

【龙芯1c库】封装时钟接口和使用示例

2017-05-08 18:21 330 查看
龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库。Git地址:http://git.oschina.NET/caogos/OpenLoongsonLib1c

本文通过“龙芯1c库”中提供的时钟相关接口,获取了PLL,CPU,DDR和APB的时钟,并将其打印出来,然后再详细讲解是如何封装这几个接口的。

龙芯1c库中时钟接口使用示例

时钟接口简介

提供几个clk_get_xxx_rate()的接口,用于获取相应的频率。比如

pll的接口为clk_get_pll_rate(),

cpu的是clk_get_cpu_rate()。

apb的是clk_get_apb_rate()。

测试思路

在main()函数中获取pll,cpu,ddr,apb的频率,并打印出来。

代码清单

#include "../lib/gpio.h"
#include "../lib/clock.h"
#include "../lib/pwm_timer.h"
#include "../lib/ls1c_regs.h"
#include "led.h"

typedef long long off_t;

struct callvectors {
int     (*open) (char *, int, int);
int     (*close) (int);
int     (*read) (int, void *, int);
int     (*write) (int, void *, int);
off_t   (*lseek) (int, off_t, int);
int     (*printf) (const char *, ...);
void    (*cacheflush) (void);
char    *(*gets) (char *);
};

struct callvectors *callvec;

#define	myprintf (*callvec->printf)
#define	mygets   (*callvec->gets)

int main(int argc, char **argv, char **env, struct callvectors *cv)
{
callvec = cv;
unsigned long pll_rate, cpu_rate, ddr_rate, apb_rate, dc_rate;
unsigned int ctrl;
volatile unsigned int *clk_div_param = (volatile unsigned int *)LS1C_CLK_DIV_PARAM;

ctrl = *clk_div_param;
myprintf("[%s] ctrl=0x%x\n", __FUNCTION__, ctrl);

pll_rate = clk_get_pll_rate();
cpu_rate = clk_get_cpu_rate();
ddr_rate = clk_get_ddr_rate();
apb_rate = clk_get_apb_rate();
dc_rate  = clk_get_dc_rate();
myprintf("[%s] pll_rate=%luHz, cpu_rate=%luHz, ddr_rate=%luHz, apb_rate=%luHz, dc_rate=%luHz\n",
__FUNCTION__, pll_rate, cpu_rate, ddr_rate, apb_rate, dc_rate);
//    test_pwm();

return(0);
}


运行结果



由测试结果可知,PLL频率为504Mhz,cpu为252Mhz,APB为126Mhz。

是不是可以修改一下让PLL=252Mhz,而cpu不用分频,直接等于PLL频率呢?这个问题留给大家了,呵呵。

龙芯1c的时钟简介

时钟结构



由上图可知,所有时钟都来自晶振。

晶振频率经过PLL后,得到一个频率,然后分频后送到了CPU;再(对CPU频率)分频后得到一个频率,送给SDRAM,APB等。

注意它们的父子关系;linux代码中也可以看出他们的父子关系,如下



目前需要关注的几个时钟是:晶振,PLL,CPU,SDRAM,APB。其中晶振的频率为24Mhz,由原理图可以知道,如下



除了晶振为外部硬件之外,其它的PLL,CPU,SDRAM和APB频率都可以通过寄存器配置。

相关寄存器

主要有两个寄存器——START_FREQ和CLK_DIV_PARAM。

PLL/SDRAM频率配置寄存器





Linux中是如何查看PLL频率的



Linux中是如何查看SDRAM频率的





这样,代码结合手册中的寄存器描述是不是很好懂啊。

CPU/CAMERA/DC频率配置寄存器





Linux中如何查看CPU频率





Linux中如何查看APB频率

APB是外设总线。比如pwm时钟源于APB。





APB频率=DDR频率从前面的时钟结构图中也可以看出来。

封装接口

接口要点

弄清楚各个时钟间的父子关系——晶振频率经过PLL后,得到一个频率,然后分频后送到了CPU;再(对CPU频率)分频后得到一个频率,送给SDRAM,APB等。

代码清单

clock.c

/*************************************************************************
*
* 时钟相关函数
*
*************************************************************************/

#include "ls1c_regs.h"

// 晶振的频率
#define AHB_CLK                 (24000000)
#define APB_CLK                 (AHB_CLK)

// START_FREQ寄存器bits
#define M_PLL_SHIFT             (8)
#define M_PLL                   (0xff << M_PLL_SHIFT)       // PLL倍频系数的整数部分
#define FRAC_N_SHIFT            (16)
#define FRAC_N                  (0xff << FRAC_N_SHIFT)      // PLL倍频系数的小数部分
#define DIV_SDRAM_SHIFT         (0)
#define DIV_SDRAM               (0x3  << DIV_SDRAM_SHIFT)

// CLK_DIV_PARAM寄存器bits
#define DIV_PIX_EN              (0x1  << 31)
#define DIV_PIX                 (0x7f << 24)
#define DIV_CAM_EN              (0x1  << 23)
#define DIV_CAM                 (0x7f << 16)
#define DIV_CPU_EN              (0x1  << 15)
#define DIV_CPU                 (0x7f << 8)
#define DIV_PIX_VALID           (0x1  << 5)
#define DIV_PIX_SEL             (0x1  << 4)
#define DIV_CAM_VALID           (0x1  << 3)
#define DIV_CAM_SEL             (0x1  << 2)
#define DIV_CPU_VALID           (0x1  << 1)
#define DIV_CPU_SEL             (0x1  << 0)

#define DIV_PIX_SHIFT           (24)
#define DIV_CAM_SHIFT           (16)
#define DIV_CPU_SHIFT           (8)

/*
* 获取PLL频率
* @ret PLL频率
*/
unsigned long clk_get_pll_rate(void)
{
volatile unsigned int *start_freq_reg = (volatile unsigned int *)LS1C_START_FREQ;
unsigned int ctrl;
unsigned long pll_rate = 0;

ctrl = *start_freq_reg;
pll_rate = (((ctrl & M_PLL) >> M_PLL_SHIFT) + ((ctrl & FRAC_N) >> FRAC_N_SHIFT)) * APB_CLK / 4;

return pll_rate;
}

/*
* 获取CPU频率
* @ret CPU频率
*/
unsigned long clk_get_cpu_rate(void)
{
unsigned long pll_rate, cpu_rate;
unsigned int ctrl;
volatile unsigned int *clk_div_param = (volatile unsigned int *)LS1C_CLK_DIV_PARAM;

pll_rate = clk_get_pll_rate();
ctrl = *clk_div_param;

// 选择时钟来源
if (DIV_CPU_SEL & ctrl)     // pll分频作为时钟信号
{
if (DIV_CPU_EN & ctrl)
{
cpu_rate = pll_rate / ((ctrl & DIV_CPU) >> DIV_CPU_SHIFT);
}
else
{
cpu_rate = pll_rate / 2;
}
}
else                        // bypass模式,晶振作为时钟输入
{
cpu_rate = APB_CLK;
}

return cpu_rate;
}

/*
* 获取DDR频率
* @ret DDR频率
*/
unsigned long clk_get_ddr_rate(void)
{
unsigned long cpu_rate, ddr_rate;
unsigned int ctrl;
volatile unsigned int *start_freq_reg = (volatile unsigned int *)LS1C_START_FREQ;

cpu_rate = clk_get_cpu_rate();
ctrl = (*start_freq_reg & DIV_SDRAM) >> DIV_SDRAM_SHIFT;

switch (ctrl)
{
case 0:
ddr_rate = cpu_rate / 2;
break;

case 1:
ddr_rate = cpu_rate / 4;
break;

case 2:
case 3:
ddr_rate = cpu_rate / 3;
break;
}

return ddr_rate;
}

/*
* 获取APB频率
* @ret APB频率
*/
unsigned long clk_get_apb_rate(void)
{
return clk_get_ddr_rate();
}

/*
* 获取DC频率
* @ret DC频率
*/
unsigned long clk_get_dc_rate(void)
{
unsigned long pll_rate, dc_rate;
unsigned int ctrl;
volatile unsigned int *clk_div_param = (volatile unsigned int *)LS1C_CLK_DIV_PARAM;

pll_rate = clk_get_pll_rate();
ctrl = *clk_div_param;

dc_rate = pll_rate / ((ctrl & DIV_PIX) >> DIV_PIX_SHIFT);

return dc_rate;
}


clock.h

/*************************************************************************
*
* 时钟相关头文件
*
*************************************************************************/

#ifndef __OPENLOONGSON_CLOCK_H
#define __OPENLOONGSON_CLOCK_H

/*
* 获取PLL频率
* @ret PLL频率
*/
unsigned long clk_get_pll_rate(void);

/*
* 获取CPU频率
* @ret CPU频率
*/
unsigned long clk_get_cpu_rate(void);

/*
* 获取DDR频率
* @ret DDR频率
*/
unsigned long clk_get_ddr_rate(void);

/*
* 获取APB频率
* @ret APB频率
*/
unsigned long clk_get_apb_rate(void);

/*
* 获取DC频率
* @ret DC频率
*/
unsigned long clk_get_dc_rate(void);

#endif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  龙芯1c 龙芯1c库