GPCNT
📎 原始文档:https://www.kdocs.cn/l/ccPnqT2pbPrf GPCNT
时钟脉冲计数器GPCNT 概述
GPCNT 为时钟脉冲计数器, 用于计算两个时钟周期的比例, 即用一个已知时钟(主时钟) 计算
另一个时钟(次时钟) 的周期
主要用于脉冲计算以及脉冲频率计算等场景(硬件级别不会占用cpu),详细可以查看用户手册寄存器
应用场景范例
- 心率传感器数据计算: 使用gpcnt,配置一个GPIO接到外部 心率传感器反馈引脚上
通过gpcnt搭配中断服务函数,来测量那个外部引脚的方波周期
代码应用示例
代码示例1(基于701-142-GPCNT)
暂略
代码示例2(使用GPCNT_TIMER作时钟源)
下面为实现代码,代码中占用GPCNT_TIMER(驱动代码用timer0)用来做主时钟源原因是因为可以动态调节主时钟源从而做到动态调节测量范围
/*gpcnt.h*/
#ifndef __GPCNT_H__
#define __GPCNT_H__
#include "board_config.h"
#include "typedef.h"
#include "asm/clock.h"
#include "cpu.h"
#include "asm/gpio.h"
//主时钟单位时间(timering_ms) GTS(32*2^n)/ 主时钟频率(定时器频率)
//如果需要调节最小单位时间,可以通过修改主时钟频率(驱动代码默认最小单位为ms)
#define INPUT_CHANNLEL3_SRC_SEL(x) SFR(JL_IOMAP->CON1, 12, 4, x)
#define INPUT_CHANNLEL2_SRC_SEL(x) SFR(JL_IOMAP->CON1, 8, 4, x)
#define INPUT_CHANNLEL1_SRC_SEL(x) SFR(JL_IOMAP->CON1, 4, 4, x)
#define GPCNT_NUM JL_GPCNT->NUM //单位时间内获取到次时钟脉冲个数
extern void gpcnt_init_reg(u32 port,u32 ms); //使能gpcnt模块
extern void gpcnt_uninit_reg(void); //关闭gpcnt模块
#endif
#include "gpcnt.h"
#include "asm/power_interface.h"
static volatile u8 is_gpcnt_active = 1; //这个变量是用来控制是否进入sniff的,在测量时候可以先设置为1,退出后再设置为0
static u8 gpcnt_idle_query(void)
{
return !is_gpcnt_active;
}
REGISTER_LP_TARGET(gpcnt_lp_target) = {
.name = "pcnt",
.is_idle = gpcnt_idle_query,
};
const u32 timer_div[16] = {
/*0000*/ 1,
/*0001*/ 4,
/*0010*/ 16,
/*0011*/ 64,
/*0100*/ 2,
/*0101*/ 8,
/*0110*/ 32,
/*0111*/ 128,
/*1000*/ 256,
/*1001*/ 4 * 256,
/*1010*/ 16 * 256,
/*1011*/ 64 * 256,
/*1100*/ 2 * 256,
/*1101*/ 8 * 256,
/*1110*/ 32 * 256,
/*1111*/ 64 * 256,
};
static u32 timering_ms = 0;
___interrupt
AT_VOLATILE_RAM_CODE
void gpcnt_isr(void)
{
u32 gpcnt_num = GPCNT_NUM;
r_printf("%dms\n",timering_ms);
r_printf("%dNUM\n",gpcnt_num);
GPCNT_EN(0);
GPCNT_CLR_PEND(TRUE);
GPCNT_EN(1);
}
void gpcnt_init_reg(u32 port,u32 ms) //可以任意传参数进来
{
r_printf("%s\n",__func__);
timering_ms = ms;
u32 u_clk = 24000000;
int div=15;
int index=15;
for(index=15;index >= 0;index--)
{
for(div=15;div >= 1;div--)
{
r_printf("index:%d div:%d\n",index,div);
u32 gpcnt_gts = 32*(1<<index);
if(((timer_div[div])*((gpcnt_gts*timering_ms)/1000)) > 0){
u32 complete_remainder = (u_clk/timer_div[div])%((gpcnt_gts*1000)/timering_ms);
u32 port_remainder = ((gpcnt_gts*1000)%timering_ms);
u32 integer = (u_clk/timer_div[div])/((gpcnt_gts*1000)/timering_ms);
printf("%d %d %d %d\n",gpcnt_gts,complete_remainder,port_remainder,integer);
if((!complete_remainder)&&(!port_remainder)&&(integer >= 0x10))
{
request_irq(IRQ_GPC_IDX,3, gpcnt_isr, 0);
GPCNT_GSS(GPCNT_GSS_INPUT_CH2);
printf("JL_GPCNT->CON 0x%x\n",JL_GPCNT->CON);
GPCNT_CSS(GPCNT_GSS_INPUT_CH3);
printf("JL_GPCNT->CON 0x%x\n",JL_GPCNT->CON);
INPUT_CHANNLEL2_SRC_SEL(8);
INPUT_CHANNLEL3_SRC_SEL(6);
gpio_set_fun_input_port(port, PFI_GP_ICH6);
gpio_set_direction(port, 1);
gpio_set_die(port, 1);
gpio_set_pull_up(port, 0);
gpio_set_pull_down(port, 1);
GPCNT_TIMER->CON = 0;
GPCNT_TIMER->CON |= (0b10 << 2); //时钟源选择STD_24M时钟源
SFR(GPCNT_TIMER->CON, 4, 4,div); //时钟源再分频
GPCNT_TIMER->CNT = 0; //清计数值
GPCNT_TIMER->PRD = (u_clk/timer_div[div])/((gpcnt_gts*1000)/timering_ms); //设置周期
GPCNT_TIMER->PWM = (GPCNT_TIMER->PRD * 5000) / 10000; //设置初始占空比,0~10000对应0~100%
GPCNT_TIMER->CON |= (0b01 << 0); //计数模式
GPCNT_TIMER->CON |= BIT(8); //PWM使能
GPCNT_GTS(index);
printf("JL_GPCNT->CON 0x%x\n",JL_GPCNT->CON);
timering_ms = 1000000/((((u_clk/timer_div[div])/integer)*1000)/gpcnt_gts);
printf("actual_timering_ms == %d\n",timering_ms); //实际设置时间以这里为准
GPCNT_EN(1);
printf("JL_GPCNT->CON 0x%x\n",JL_GPCNT->CON);
return;
}
}
}
}
printf("没有找到合适的参数,请修改时间\n"); //最好就是2 4 8 16之类时间,否则会出现实际时间不准
}
void gpcnt_uninit_reg(void)
{
GPCNT_EN(0);
GPCNT_CLR_PEND(TRUE);
unrequest_irq(IRQ_GPC_IDX);
gpio_disable_fun_input_port(PFI_GP_ICH6);
GPCNT_TIMER->CON |= (0b00 << 0); //关闭定时器
}