ARM40-A5D27应用程序——脉冲计数
2020.6.10
在工业控制中,经常需要获取脉冲信号计数值、频率、周期等参数。本文为实现外部输入脉冲信号的计数、频率、周期测量的实例。
主要功能: 读取1s时间内的外部输入脉冲信号计数值,通过/proc/flowmeter可以查看结果。
一、脉冲计数
(1)C语言源码
文件包含 flow_meter.c,Makefile,代码见附录(1)。
(2)交叉编译
make
(3)执行程序与测试
将交叉编译得到的flowmeter.ko文件拷贝到ARM40-A5D27板中,执行程序:
# insmod flowmerter.ko
flowmeter: loading out-of-tree module taints kernel.
# cat /proc/flowmeter
1972 1972 0 0 0 0 0 0 0 0 0 0
参考文章:
《Linux Device Drivers Development: Develop customized drivers for embedded Linux》
《ARM40-A5应用——W1LED的使用说明》
荟聚计划:共商 共建 共享 Grant
附:
(1)flow_meter.c 的代码
/*
* PC09~PC18, PC21, PC22, 共12路DIx信号,其中DI1,DI2为高速数字输入,
* 脉冲计数范围2Hz~2kHz;其余10路DIx脉冲计数范围2HZ~500HZ。
* 计数范围仅为示例,最大频率以实测为准。
* 原理:PB10提供1HZ的定时信号;在PB10中断响应函数中,统计这1s时间
* 内,其它DIx共产生多少个中断,因而得到DIx上的脉冲个数。
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#define GPIO_TOTAL 13
#define GPIO_HZ_REF 42 //PB10 is 1HZ
//GPIO_DI: PC09~PC18, PC21, PC22 (73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 85, 86)
int GPIO_DI[GPIO_TOTAL] = {GPIO_HZ_REF, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 85, 86};
int irq_sum[GPIO_TOTAL] = {0, };
typedef struct
{
int gpio;
int irq;
int irq_rate;
char name[6];
} gpio_info_t;
gpio_info_t *gpio_info = NULL;
static irqreturn_t flowmeter_isr(int irq, void *dev_id)
{
unsigned long flags = 0;
int i;
gpio_info_t *data = dev_id;
local_irq_save(flags);
//printk("%s %s\tgpio:%d, irq: %d\n", __func__, data->name, data->gpio, data->irq);
data->irq_rate ++;
if(data == gpio_info) { //if GPIO_HZ_REF, the 1HZ irq is coming, restore the irq_sum
for(i = 1; i < GPIO_TOTAL; i++) {
irq_sum[i] = (data + i)->irq_rate;
(data + i)->irq_rate = 0;
}
}
local_irq_restore(flags);
return IRQ_HANDLED;
}
static int flowmeter_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "%d %d %d %d %d %d %d %d %d %d %d %d\n",
irq_sum[1], irq_sum[2], irq_sum[3], irq_sum[4], irq_sum[5], irq_sum[6],
irq_sum[7], irq_sum[8], irq_sum[9], irq_sum[10], irq_sum[11], irq_sum[12]);
return 0;
}
static int __init flowmeter_init(void)
{
int ret;
int i;
proc_create_single("flowmeter", 0, NULL, flowmeter_proc_show);
gpio_info = kzalloc(GPIO_TOTAL * sizeof(*gpio_info), GFP_KERNEL);
if (!gpio_info)
return -ENOMEM;
//DI_0 is GPIO_HZ_REF
for (i = 0; i < GPIO_TOTAL; i++) {
sprintf(gpio_info[i].name, "DI_%d", i);
gpio_info[i].gpio = GPIO_DI[i];
gpio_info[i].irq_rate = 0;
gpio_info[i].irq = gpio_to_irq(gpio_info[i].gpio);
if(gpio_info[i].irq < 0) {
printk("gpio_to_irq %d failed\n", gpio_info[i].gpio);
return -1;
}
ret = request_irq(gpio_info[i].irq, flowmeter_isr, IRQ_TYPE_EDGE_FALLING, gpio_info[i].name, gpio_info+i);
if(ret < 0) {
printk("request_irq %d failed\n", gpio_info[i].gpio);
return -1;
}
}
return 0;
}
static void __exit flowmeter_exit(void)
{
int i;
for (i = 0; i < GPIO_TOTAL; i++) {
free_irq(gpio_info[i].irq, gpio_info+i);
}
remove_proc_entry("flowmeter", NULL);
}
module_init(flowmeter_init);
module_exit(flowmeter_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FENGWEI");
(2)Makefile 的代码
MODULE_NAME:=flowmeter
obj-m += $(MODULE_NAME).o
$(MODULE_NAME)-objs :=flow_meter.o
PWD := $(shell pwd)
KDIR = /home/arm40_a5d2/arm40_a5d2_kernel/linux-5.4-at91
all:
make ARCH=arm \
CROSS_COMPILE=/home/arm40_a5d2/arm40_armgcc/gcc-linaro-7.3.1-2018.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- \
-C $(KDIR) M=$(PWD) modules
rm -rf *.o *.mod.c *.symvers *.order
clean:
rm *.ko
(3)仅测量高速数字输入通道示例
/*
* PC09~PC10为2路高速DIx信号,脉冲计数范围2Hz~2kHz;
* 计数范围仅为示例,最大频率以实测为准。
* 原理:PB10提供1HZ的定时信号;在PB10中断响应函数中,统计这1s时间
* 内,DI1、DI2上共产生多少个中断,因而得到DI1、DI2上的脉冲个数。
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#define GPIO_TOTAL 3
#define GPIO_HZ_REF 42 //PB10 is 1HZ
//GPIO_DI: PC09~PC10 (73, 74)
int GPIO_DI[GPIO_TOTAL] = {GPIO_HZ_REF, 73, 74};
int irq_sum[GPIO_TOTAL] = {0, };
typedef struct
{
int gpio;
int irq;
int irq_rate;
char name[6];
} gpio_info_t;
gpio_info_t *gpio_info = NULL;
static irqreturn_t flowmeter_isr(int irq, void *dev_id)
{
unsigned long flags = 0;
int i;
gpio_info_t *data = dev_id;
local_irq_save(flags);
//printk("%s %s\tgpio:%d, irq: %d\n", __func__, data->name, data->gpio, data->irq);
data->irq_rate ++;
if(data == gpio_info) { //if GPIO_HZ_REF, the 1HZ irq is coming, restore the irq_sum
for(i = 1; i < GPIO_TOTAL; i++) {
irq_sum[i] = (data + i)->irq_rate;
(data + i)->irq_rate = 0;
}
}
local_irq_restore(flags);
return IRQ_HANDLED;
}
static int flowmeter_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "%d %d\n", irq_sum[1], irq_sum[2]);
return 0;
}
static int __init flowmeter_init(void)
{
int ret;
int i;
proc_create_single("flowmeter", 0, NULL, flowmeter_proc_show);
gpio_info = kzalloc(GPIO_TOTAL * sizeof(*gpio_info), GFP_KERNEL);
if (!gpio_info)
return -ENOMEM;
//DI_0 is GPIO_HZ_REF
for (i = 0; i < GPIO_TOTAL; i++) {
sprintf(gpio_info[i].name, "DI_%d", i);
gpio_info[i].gpio = GPIO_DI[i];
gpio_info[i].irq_rate = 0;
gpio_info[i].irq = gpio_to_irq(gpio_info[i].gpio);
if(gpio_info[i].irq < 0) {
printk("gpio_to_irq %d failed\n", gpio_info[i].gpio);
return -1;
}
ret = request_irq(gpio_info[i].irq, flowmeter_isr, IRQ_TYPE_EDGE_FALLING, gpio_info[i].name, gpio_info+i);
if(ret < 0) {
printk("request_irq %d failed\n", gpio_info[i].gpio);
return -1;
}
}
return 0;
}
static void __exit flowmeter_exit(void)
{
int i;
for (i = 0; i < GPIO_TOTAL; i++) {
free_irq(gpio_info[i].irq, gpio_info+i);
}
remove_proc_entry("flowmeter", NULL);
}
module_init(flowmeter_init);
module_exit(flowmeter_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FENGWEI");