【龙印】把龙芯1c的pwm用作定时器并产生中断
本文为在用龙芯1c做3D打印机过程中的笔记。龙芯1c做的3d打印机简称“龙印”
3d打印机固件marlin巧妙运用定时器让整个固件不必依赖实时操作系统,即把对实时性要求较高的部分巧妙的用定时器中断来实现了。
marlin固件的原理分析请参考《3D打印机:FPGA+Nios_ii移植Marlin固件二:Marlin固件的详细分析》 http://blog.sina.com.cn/s/blog_679933490102vv8z.html
由此可见定时器中断在marlin中的重要性。这里把龙芯1c的pwm用作定时器,而不输出pwm脉冲,并定时产生中断。不多说了上源码
源码“ls1c_pwm_timer.c”
/* * drivers\misc\ls1c_pwm_timer.c* 把龙芯1c的pwm用作定时器,定时产生中断*/#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/ls1c_3dprinter_motor.h>
#include <linux/delay.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/kfifo.h>// 定时器默认的定时时间(单位ns)
#define PWM_TIMER_DEF_TIME_NS (1*1000*1000*1000) // 1s// 寄存器偏移
#define REG_PWM_CNTR 0x00
#define REG_PWM_HRC 0x04
#define REG_PWM_LRC 0x08
#define REG_PWM_CTRL 0x0c// pwm控制寄存器的每个bit
#define LS1C_PWM_INT_LRC_EN (11) // 低脉冲计数器中断使能
#define LS1C_PWM_INT_HRC_EN (10) // 高脉冲计数器中断使能
#define LS1C_PWM_CNTR_RST (7) // CNTR计数器清零
#define LS1C_PWM_INT_SR (6) // 中断状态位
#define LS1C_PWM_INTEN (5) // 中断使能位
#define LS1C_PWM_OE (3) // 脉冲输出使能控制位
#define LS1C_PWM_CNT_EN (0) // CNTR使能位static void __iomem *pwm_timer_reg_base = NULL; // 映射后的寄存器基地址
static struct workqueue_struct *pwm_timer_queue; // 定时器中断的下半部--工作队列
static struct work_struct pwm_timer_work;
static unsigned long long pwm_timer_clk_rate; // pwm的计数器的时钟频率
static DEFINE_MUTEX(pwm_timer_lock);// 设置定时器时间
// @period_ns 定时器时间,单位(ns)
static void pwm_timer_set_time(unsigned long period_ns)
{unsigned long long tmp = 0;tmp = pwm_timer_clk_rate * period_ns;do_div(tmp, 1000000000);writel(tmp, pwm_timer_reg_base+REG_PWM_LRC);printk(KERN_DEBUG "[%s] pwm_timer_clk_rate=%llu, REG_PWM_LRC's value=%llu\n",__FUNCTION__,pwm_timer_clk_rate,tmp);return ;
}static void pwm_timer_enable(void)
{unsigned int tmp = 0;tmp = readl(pwm_timer_reg_base+REG_PWM_CTRL);tmp |= (1<<LS1C_PWM_CNT_EN);writel(tmp, pwm_timer_reg_base+REG_PWM_CTRL);return ;
}static void pwm_timer_disable(void)
{unsigned int tmp = 0;tmp = readl(pwm_timer_reg_base+REG_PWM_CTRL);tmp &= ~(1<<LS1C_PWM_CNT_EN);writel(tmp, pwm_timer_reg_base+REG_PWM_CTRL);return ;
}// 重新启动定时器
// 专门为定时器中断封装的函数,
// pwm定时器中断后,如果不调此函数重启定时器,则整个系统卡死
static void pwm_timer_restart(void)
{writel(0x829, pwm_timer_reg_base+REG_PWM_CTRL);return ;
}// 定时器中断处理函数(上半部)
static irqreturn_t pwm_timer_irq_handler(int irq, void *devid)
{// 重新启动定时器// 如果不重启,则整个系统卡死pwm_timer_restart();queue_work(pwm_timer_queue, &pwm_timer_work);return IRQ_HANDLED;
}// 定时器中断的下半部
static void pwm_timer_queue_handler(struct work_struct *work)
{static int count = 0;// 这里仅仅为了演示,所以打印一条消息到串口// 实际实用中,在中断处理函数中使用打印函数应该非常谨慎,// 哪怕是在中断程序的下半部,// 比如,如果定时时间比打印该消息的时间还短,就有问题,// 所以这里为了演示,定时时间设为1s// 中断处理程序的上半部严禁使用打印函数,printk(KERN_DEBUG "[%s] one irq. count=%d\n", __FUNCTION__, count);count++;return ;
}static int pwm_timer_open(struct inode *inode, struct file *filp)
{return 0;
}static int pwm_timer_close(struct inode *inode, struct file *filp)
{// 禁用定时器pwm_timer_disable();return 0;
}static ssize_t pwm_timer_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp)
{int ret = 0;unsigned long period_ns = 0; // 定时器定时时间if (mutex_lock_interruptible(&pwm_timer_lock)){return -ERESTARTSYS;}ret = copy_from_user(&period_ns, buf, sizeof(period_ns));mutex_unlock(&pwm_timer_lock);if (ret){printk(KERN_ERR "[%s] write count err. count=%d\n", __FUNCTION__, count);return -1;}printk(KERN_DEBUG "[%s] period_ns=%lu\n", __FUNCTION__, period_ns);// 设置定时器时间,并启动pwm_timer_set_time(period_ns);writel(0, pwm_timer_reg_base+REG_PWM_CNTR); // 计数器清零pwm_timer_enable();return sizeof(period_ns);
}static struct file_operations ls1c_pwm_timer_ops = {.owner = THIS_MODULE,.open = pwm_timer_open,.release = pwm_timer_close,.write = pwm_timer_write,
};static struct miscdevice ls1c_pwm_timer_miscdev = {.minor = MISC_DYNAMIC_MINOR,.name = "ls1c_pwm_timer",.fops = &ls1c_pwm_timer_ops,
};static int pwm_timer_probe(struct platform_device *pdev)
{int ret = 0;int irq = 0;struct resource *res = NULL;struct clk *pwm_clk = NULL;res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (NULL == res){printk(KERN_ERR "[%s] no IO memory resource defined.\n", __FUNCTION__);return -ENODEV;}res = request_mem_region(res->start, resource_size(res), pdev->name);if (NULL == res){printk(KERN_ERR "[%s] failed to request memory resource.\n", __FUNCTION__);return -EBUSY;}pwm_timer_reg_base = ioremap(res->start, resource_size(res));if (NULL == pwm_timer_reg_base){printk(KERN_ERR "[%s] ioremap pwm register fail.\n", __FUNCTION__);ret = -ENODEV;goto fail_free_res;}irq = platform_get_irq(pdev, 0);if (0 > irq){printk(KERN_ERR "[%s] no IRQ resource defined.\n", __FUNCTION__);ret = -ENXIO;goto fail_free_io;}ret = request_irq(irq, pwm_timer_irq_handler, IRQF_DISABLED, pdev->name, NULL);if (0 > ret){printk(KERN_ERR "[%s] failed to request IRQ.\n", __FUNCTION__);goto fail_free_io;}pwm_timer_queue = create_workqueue("pwm_timer_queue");if (!pwm_timer_queue){printk(KERN_ERR "[%s] failed to create workqueue.\n", __FUNCTION__);ret = -EBUSY;goto fail_free_irq;}INIT_WORK(&pwm_timer_work, pwm_timer_queue_handler);// 获取pwm计数器的时钟pwm_clk = clk_get(NULL, "apb");if (IS_ERR(pwm_clk)){ret = PTR_ERR(pwm_clk);pwm_clk = NULL;printk(KERN_ERR "[%s] get pwm clk fail.\n", __FUNCTION__);goto fail_destroy_workqueue;}pwm_timer_clk_rate = (unsigned long long)clk_get_rate(pwm_clk);clk_put(pwm_clk);// 设置定时器时间pwm_timer_set_time(PWM_TIMER_DEF_TIME_NS);// 设置控制寄存器// 低脉冲计数器中断使能// 高脉冲计数器中断禁止// CNTR计数器正常工作// 中断使能// 屏蔽脉冲输出// CNTR停止计数writel(0x828, pwm_timer_reg_base+REG_PWM_CTRL);return 0;fail_destroy_workqueue:destroy_workqueue(pwm_timer_queue);
fail_free_irq:free_irq(irq, NULL);
fail_free_io:iounmap(pwm_timer_reg_base);
fail_free_res:release_mem_region(res->start, resource_size(res));return ret;
}static int pwm_timer_remove(struct platform_device *pdev)
{int irq = 0;struct resource *res = NULL;destroy_workqueue(pwm_timer_queue);irq = platform_get_irq(pdev, 0);if (0 <= irq){free_irq(irq, NULL);}iounmap(pwm_timer_reg_base);res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (NULL != res){release_mem_region(res->start, resource_size(res));}return 0;
}static struct platform_driver ls1c_pwm_timer_driver = {.driver = {.name = "ls1c_pwm_timer",.owner = THIS_MODULE,},.probe = pwm_timer_probe,.remove = pwm_timer_remove,
};static int __init pwm_timer_init(void)
{if (misc_register(&ls1c_pwm_timer_miscdev)){printk(KERN_ERR "could not register pwm timer driver!\n");return -EBUSY;}return platform_driver_register(&ls1c_pwm_timer_driver);
}static void __exit pwm_timer_exit(void)
{misc_deregister(&ls1c_pwm_timer_miscdev);platform_driver_unregister(&ls1c_pwm_timer_driver);
}module_init(pwm_timer_init);
module_exit(pwm_timer_exit);
把源文件“ls1c_pwm_timer.c”放到目录“drivers\misc”下
在“arch\mips\loongson\ls1x\ls1c\platform.c”中增加
#ifdef CONFIG_LS1C_PWM_TIMER
static struct resource ls1c_pwm_timer_resources[] = {
{
.start = LS1X_PWM3_BASE,
.end = LS1X_PWM3_BASE + 0x10 -1,
.flags = IORESOURCE_MEM,
},{
.start = LS1X_PWM3_IRQ,
.end = LS1X_PWM3_IRQ,
.flags = IORESOURCE_IRQ,
}
};
static struct platform_device ls1c_pwm_timer = {
.name = "ls1c_pwm_timer",
.resource = ls1c_pwm_timer_resources,
.num_resources = ARRAY_SIZE(ls1c_pwm_timer_resources),
};
#endif //End of CONFIG_LS1C_PWM_TIMER
在“static struct platform_device *ls1b_platform_devices[] __initdata”中增加
#ifdef CONFIG_LS1C_PWM_TIMER
&ls1c_pwm_timer,
#endif
在“drivers\misc\Kconfig”中增加
config LS1C_PWM_TIMER
tristate "ls1c pwm timer"
depends on LS1C_MACH
help
Say Y here if you want to build a pwm timer driver for ls1c
在“drivers\misc\Makefile”中增加
obj-$(CONFIG_LS1C_PWM_TIMER) += ls1c_pwm_timer.o
配置
make menuconfig
Device Drivers --->
[*] Misc devices --->
<*> ls1c pwm timer
测试用的应用程序“main.c”
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(void)
{int fd = 0;unsigned long period_ns = 100000000; // 定时1sint ret = 0;fd = open("/dev/ls1c_pwm_timer", O_RDWR);if (-1 == fd){printf("[%s] open device file /dev/ls1c_pwm_timer fail.\n", __FUNCTION__);return -1;}ret = write(fd, &period_ns, sizeof(period_ns));if (sizeof(period_ns) != ret){close(fd);printf("[%s] write fail. ret=%d\n", __FUNCTION__, ret);return -1;}while (1){sleep(1);}
}
测试效果
测试时需要用命令“echo 8 > /proc/sys/kernel/printk”把打印级别调到调试模式,才能看到打印。
本示例是将定时器时间设置为1s,并通过在中断下半部中打印一条信息来演示的。再次提醒中断程序的上半部中不能使用打印函数,因为打印函数太耗时了。下半部中使用打印函数也要非常谨慎。
在marlin的实现中,定时器中断中需要产生一个高电平脉冲给步进电机驱动芯片,步进电机驱动芯片收到一个脉冲就让步进电机就走一步,这样就实现了控制步进电机的目的。以A4988步进电机驱动芯片为例,A4988需要一个脉冲的高电平时间和低电平时间至少为1us。1us对于cpu频率为260Mhz的龙芯1c来说是很长的,特别是在中断中,所以这里使用了中断下半部来解决这个问题。在下半部中可以用udelay(2)来实现。注意udelay()是硬延时。
如果想在io口输出pwm波形,而不想用定时器,那就直接使用pwm功能,把中断禁止,使能脉冲输出。当然linux源码中已经封装好了,请查看文件“arch\mips\loongson\ls1x\pwm.c”
在使用龙芯1c的pwm时遇到如下麻烦,都找到了解决办法,讲经验记录与此
1,pwm的CTRL寄存器中INT_SR
1c的用户手册中说:在INT_SR中写入1,可以清中断。
在linux驱动的probe函数中,向INT_SR中写入了1,结果linux启动到写入1哪里后卡死。
我的理解是,系统刚刚启动,没有中断,而我写1清中断了,所以卡死。
行嘛,这个还可以理解,请继续往下看。
2,pwm的CTRL寄存器中CNTR_RST
1c的用户手册中说:置1时,CNTR计数器清零;置0时,CNTR计数器正常工作。
同样在linux驱动初始化时,我把CNTR_RST置了1,,结果始终没有产生中断,定位发现——CNTR计数器一直为0,根本没有正常计数。
大哥,你没说,清零后计数器不正常工作(“不计数”)好不好,不带这样玩的。
3,pwm作为定时器产生中断后成功进入中断,中断处理程序中点亮led,然后返回,结果中断后卡死
没找到原因,刚开始以为需要手动清中断标志,向CTRL寄存器中的INT_SR写1,还是不能解决。
最后参考了linux源码中的“ls1x_pwm_audio.c”,发现中断处理程序中重新设置了一遍CTRL寄存器。就OK了。比如我重新写入0x829.
【龙印】把龙芯1c的pwm用作定时器并产生中断相关推荐
- 【龙印】用龙芯1c实现3D打印机的总体思路
热熔型3d打印机的工作原理 控制打印头不停的运动,在需要打印的地方将耗材融化并挤出来.就像蜘蛛织网一样,当蜘蛛的网线够大,同时网格够小,那么是不是相邻两格的网线就紧挨着了.我就是这么理解热熔型3d打印 ...
- 【龙芯1c库】封装硬件定时器接口和使用示例
龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库.完整源码请移步到https://gitee.com/caogos/OpenLoongsonLib1c 龙芯1c库中硬件定时器 ...
- 【龙芯1c库】封装硬件pwm接口和使用示例
龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库.Git地址:https://gitee.com/caogos/OpenLoongsonLib1c 本文通过"龙芯1 ...
- 【龙印】用龙芯1c的硬件pwm产生单个脉冲来驱动步进电机
本文为在用龙芯1c做3D打印机过程中的笔记.龙芯1c做的3d打印机简称"龙印",Git地址"http://git.oschina.NET/caogos/marlin_ls ...
- 龙芯处理器可以适配鸿蒙os吗,SylixOS龙芯1C适配总结
1.龙芯1C简介 1.1龙芯1C简介 龙芯 1C300(以下简称 1C)芯片是基于 LS232 处理器核的高性价比单芯片系统,可应用于指纹生物识别.物联传感等领域.1C 包含浮点处理单元,可以有效增强 ...
- 【龙芯1c库】封装模拟I2C接口和使用示例
龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库.Git地址:https://gitee.com/caogos/OpenLoongsonLib1c I2C接口是常用的接口之一 ...
- 用龙芯1c库实现无源蜂鸣器唱歌《送别》
龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,可用于裸机编程和实时系统,类似于STM32库.Git地址:http://git.oschina.NET/caogos/OpenLoongsonL ...
- Buildroot 龙芯1C支持指南
本文转载自:https://github.com/pengphei/smartloong-sphinx/blob/master/source/cn/loongson1c_buildroot_guide ...
- 【龙芯1c库】封装CAN接口和使用示例
can使用还是比较广泛的,之前有网友在龙芯1b和龙芯1c上已经测试过了在裸机编程中使用CAN接口,这里把他们分享的程序贴上来,供大家参考. 龙芯1b上的测试程序在https://gitee.com/c ...
最新文章
- 蓝牙杂散超标_杂散发射
- linux 查看文件中数据类型,Linux下使用file命令确定文件中数据的类型-文件类型...
- Python中join 和 split详解
- 玩点创意编程,发现另一个世界
- [精品]CSAPP Bomb Lab 解题报告(六)
- CSS3(七) 前端预处理技术(Less、Sass、CoffeeScript)
- STM32 低功耗STOP模式,RTC唤醒
- python实现LU分解
- 实验室新生成长指南[2.2.1] · 连接器
- ECCV 2020 论文大盘点-图像增强与图像恢复篇
- 百度有啊转型生活平台 启用新标与框计算对接
- Cisco WLC和瘦ap的配置
- 180701 icon文件查找与转换网站
- java 离线语音识别
- Reinforced History Backtracking for Conversational Question Answering论文翻译
- 四川企立方:拼多多不开推广能做起来吗
- CTF-攻防世界web新手入门篇
- vivo便签批量导出为excel/记事本/word
- GIS领域的一些机器学习和人工智能的案例
- 美剧《反恐24小时》
热门文章
- halcon脚本-找直线并拟合
- 回溯算法--LeetCode-78 子集、LeetCode-90 子集Ⅱ
- 深入理解iOS APP启动过程
- linux_系统帮助propos/whatis/which/whereis/man/info/help...)/bashzsh/build-in command帮助/wildcard/regex
- windows下查看错误码与错误信息
- 1072: 青蛙爬井 Java
- 白盒与黑盒测试什么区分
- Unity-点击屏幕进行移动
- NGUI制作Word图文混排效果
- 【配送路径规划】基于matlab蚁群算法求解配送路径最短问题【含Matlab源码 2222期】