本文为在用龙芯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用作定时器并产生中断相关推荐

  1. 【龙印】用龙芯1c实现3D打印机的总体思路

    热熔型3d打印机的工作原理 控制打印头不停的运动,在需要打印的地方将耗材融化并挤出来.就像蜘蛛织网一样,当蜘蛛的网线够大,同时网格够小,那么是不是相邻两格的网线就紧挨着了.我就是这么理解热熔型3d打印 ...

  2. 【龙芯1c库】封装硬件定时器接口和使用示例

    龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库.完整源码请移步到https://gitee.com/caogos/OpenLoongsonLib1c 龙芯1c库中硬件定时器 ...

  3. 【龙芯1c库】封装硬件pwm接口和使用示例

    龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库.Git地址:https://gitee.com/caogos/OpenLoongsonLib1c 本文通过"龙芯1 ...

  4. 【龙印】用龙芯1c的硬件pwm产生单个脉冲来驱动步进电机

    本文为在用龙芯1c做3D打印机过程中的笔记.龙芯1c做的3d打印机简称"龙印",Git地址"http://git.oschina.NET/caogos/marlin_ls ...

  5. 龙芯处理器可以适配鸿蒙os吗,SylixOS龙芯1C适配总结

    1.龙芯1C简介 1.1龙芯1C简介 龙芯 1C300(以下简称 1C)芯片是基于 LS232 处理器核的高性价比单芯片系统,可应用于指纹生物识别.物联传感等领域.1C 包含浮点处理单元,可以有效增强 ...

  6. 【龙芯1c库】封装模拟I2C接口和使用示例

    龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库.Git地址:https://gitee.com/caogos/OpenLoongsonLib1c I2C接口是常用的接口之一 ...

  7. 用龙芯1c库实现无源蜂鸣器唱歌《送别》

    龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,可用于裸机编程和实时系统,类似于STM32库.Git地址:http://git.oschina.NET/caogos/OpenLoongsonL ...

  8. Buildroot 龙芯1C支持指南

    本文转载自:https://github.com/pengphei/smartloong-sphinx/blob/master/source/cn/loongson1c_buildroot_guide ...

  9. 【龙芯1c库】封装CAN接口和使用示例

    can使用还是比较广泛的,之前有网友在龙芯1b和龙芯1c上已经测试过了在裸机编程中使用CAN接口,这里把他们分享的程序贴上来,供大家参考. 龙芯1b上的测试程序在https://gitee.com/c ...

最新文章

  1. 蓝牙杂散超标_杂散发射
  2. linux 查看文件中数据类型,Linux下使用file命令确定文件中数据的类型-文件类型...
  3. Python中join 和 split详解
  4. 玩点创意编程,发现另一个世界
  5. [精品]CSAPP Bomb Lab 解题报告(六)
  6. CSS3(七) 前端预处理技术(Less、Sass、CoffeeScript)
  7. STM32 低功耗STOP模式,RTC唤醒
  8. python实现LU分解
  9. 实验室新生成长指南[2.2.1] · 连接器
  10. ECCV 2020 论文大盘点-图像增强与图像恢复篇
  11. 百度有啊转型生活平台 启用新标与框计算对接
  12. Cisco WLC和瘦ap的配置
  13. 180701 icon文件查找与转换网站
  14. java 离线语音识别
  15. Reinforced History Backtracking for Conversational Question Answering论文翻译
  16. 四川企立方:拼多多不开推广能做起来吗
  17. CTF-攻防世界web新手入门篇
  18. vivo便签批量导出为excel/记事本/word
  19. GIS领域的一些机器学习和人工智能的案例
  20. 美剧《反恐24小时》

热门文章

  1. halcon脚本-找直线并拟合
  2. 回溯算法--LeetCode-78 子集、LeetCode-90 子集Ⅱ
  3. 深入理解iOS APP启动过程
  4. linux_系统帮助propos/whatis/which/whereis/man/info/help...)/bashzsh/build-in command帮助/wildcard/regex
  5. windows下查看错误码与错误信息
  6. 1072: 青蛙爬井 Java
  7. 白盒与黑盒测试什么区分
  8. Unity-点击屏幕进行移动
  9. NGUI制作Word图文混排效果
  10. 【配送路径规划】基于matlab蚁群算法求解配送路径最短问题【含Matlab源码 2222期】