本文共 6083 字,大约阅读时间需要 20 分钟。
龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库。Git地址:https://gitee.com/caogos/OpenLoongsonLib1c本文通过“龙芯1c库”中提供的时钟相关接口,获取了PLL,CPU,DDR和APB的时钟,并将其打印出来,然后再详细讲解是如何封装这几个接口的。
提供几个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频率呢?这个问题留给大家了,呵呵。由上图可知,所有时钟都来自晶振。
晶振频率经过PLL后,得到一个频率,然后分频后送到了CPU;再(对CPU频率)分频后得到一个频率,送给SDRAM,APB等。注意它们的父子关系;linux代码中也可以看出他们的父子关系,如下目前需要关注的几个时钟是:晶振,PLL,CPU,SDRAM,APB。其中晶振的频率为24Mhz,由原理图可以知道,如下
除了晶振为外部硬件之外,其它的PLL,CPU,SDRAM和APB频率都可以通过寄存器配置。
主要有两个寄存器——START_FREQ和CLK_DIV_PARAM。
这样,代码结合手册中的寄存器描述是不是很好懂啊。
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