仅作为自我记录的一个demo
本次GPIO以微妙级别频率的反转实验有以下几个启示:
- 一开始在应用层做延时,来实现2微妙周期,占空比50%的GPIO反转,发现波形的频率一直上不去,只能在25hz徘徊,后来索性去掉延时,发现最高也就只能到发现只能到达6.5us周期,一开始是认为system函数内有系统调用,开销比较大,但是后来一想再大应该超不过200ns,后来向组内大佬请求帮助,大佬说sleep函数会引起睡眠,造成进程切换,这个开销是很大的,特别是us级别的延时,得不偿失。另外,一开始操作gpio是用system来执行echo操作gpio,这个过程本质是execvp了进程,也有一定开销。应该用file操作sysfs的文件。
- 后来将延时操作迁移到linux内核驱动,这次不加延时,最高可以跑到120ns的周期,我想应该还能跑到更高,因为这个demo是开启一个内核线程去执行的。
- 最后,将延时操作换为对时间的读取,使用gettimeofday函数,这个函数的精度刚好是us级别,通过在while循环里判断两次读取时间查是否大于等于1来反转gpio,到这个做法好像不太行,输出的波形不稳定,频率在不断变化:
- 放弃3中的做法后,可以尝试的方法还有内核定时器,udelay函数可以尝试。看看尝试了udelay的效果:
效果还行,最终就选择了这个方案。
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/kthread.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/module.h>#define ENTER() printk(KERN_DEBUG "%s() Enter", __func__)
#define EXIT() printk(KERN_DEBUG "%s() Exit", __func__)
#define ERR(fmt, args...) printk(KERN_ERR "%s()-%d: " fmt "\n", __func__, __LINE__, ##args)
#define DBG(fmt, args...) printk(KERN_DEBUG "%s()-%d: " fmt "\n", __func__, __LINE__, ##args)/** toggle the gpio output value every 1us
*/#define GPIO_PORTB_BASE 0xF244C000
#define GPIOB_DATA_OFF 0x0
#define GPIOB_DIR_OFF 0x4#define IOMUX_BASE 0xF0040500
#define DRIVER_ABLT_R2R_OFF 0x00
#define PINCTRL_OFF 0x2C
#define GPIO_FUNC_NUM 8
#define DRIVER_SET_VAL 1
#define GPIO_PIN_NUM 4
#define PIN_MUX_BIT_WIDTH 4
#define PIN_DRV_BIT_WIDTH 1#define TOGGLE_SPAN_IN_US 1static unsigned int *gpio_handler;
static unsigned int *iomux_handler;
static struct task_struct *test_kthread = NULL; static void pinctrl(void *handler)
{unsigned int *pinctrl_banke = (unsigned int *)(handler + PINCTRL_OFF);unsigned int *driver_banke = (unsigned int *)(handler + DRIVER_ABLT_R2R_OFF);// PE4 set func 8*pinctrl_banke = (unsigned int)(GPIO_FUNC_NUM << (GPIO_PIN_NUM * PIN_MUX_BIT_WIDTH));// PE4 driver ability set 2ma*driver_banke = (unsigned int)(DRIVER_SET_VAL << (GPIO_PIN_NUM * PIN_DRV_BIT_WIDTH));
}static void gpio_cfg(void *handler)
{unsigned int *gpio_dir_base = (unsigned int *)(handler + GPIOB_DIR_OFF);//set gpiob1 direction output *gpio_dir_base = 0x02;
}inline void gpio_set_high(void *handler)
{unsigned int * gpio_dat_base = (unsigned int *)(handler + GPIOB_DATA_OFF);//set gpiob1 output high*gpio_dat_base = 0x02;
}inline void gpio_set_low(void *handler)
{unsigned int * gpio_dat_base = (unsigned int *)(handler + GPIOB_DATA_OFF);//set gpiob1 output low*gpio_dat_base = 0x00;
}static int gpio_toggle_thread(void* data)
{static volatile unsigned int flg = 0;ENTER(); gpio_handler = ioremap(GPIO_PORTB_BASE, 8);iomux_handler = ioremap(IOMUX_BASE, 50);pr_err("gpio_toggle_thread start \r\n");pinctrl(iomux_handler);gpio_cfg(gpio_handler);while(!kthread_should_stop()){if(flg == 0){gpio_set_high(gpio_handler);flg = 1;} else {gpio_set_low(gpio_handler);flg = 0;}udelay(1);}EXIT();return 0;
}int __init gp_toggle_init(void)
{ENTER();test_kthread = kthread_run(gpio_toggle_thread, NULL, "gpio-toggle"); if(!test_kthread) {ERR("kthread_run fail");return -ECHILD; }EXIT();return 0;
}void __exit gp_toggle_exit(void)
{ENTER();if (test_kthread) {DBG("kthread_stop");kthread_stop(test_kthread); //停止内核线程test_kthread = NULL;}EXIT(); return;
}module_init(gp_toggle_init);
module_exit(gp_toggle_exit);
MODULE_LICENSE("GPL");