【龙印】用龙芯1c的硬件pwm产生单个脉冲来驱动步进电机
来源:互联网 发布:数据维护工具v3.8 编辑:程序博客网 时间:2024/06/05 19:58
本文为在用龙芯1c做3D打印机过程中的笔记。龙芯1c做的3d打印机简称“龙印”,交流论坛地址是“http://www.openloongson.org/”,Git地址“http://git.oschina.NET/caogos/marlin_ls1c”
以步进电机驱动芯片A4988为例,给A4988一个脉冲,A4988就会驱动步进电机“走”一步(假设细分为1),在1秒内脉冲个数就决定了步进电机的速度。在marlin源码中,是通过在定时器中断里面将IO口拉高然后延时再拉低来产生一个脉冲的。很显然,通过这种延时的方式来产生脉冲会消耗大量的cpu资源,恰好龙芯1c的硬件pwm可以产生单个脉冲,这样就不必在定时器中断中延时了,大大降低了cpu占有率,当步进电机速度越快时,效果越明显。
硬件说明
龙芯1c共有4个pwm,其中pwm0和pwm1可以直接使用,pwm2和pwm3需要复用。pwm2和pwm3可以在多个引脚上复用,比如pwm2可以与CAMDATA2/GPIO52复用,也可以与CAMPCLKIN/GPIO46复用。由于智龙v2.1的板子上,CAMDATA2/GPIO52接有led,所以选择将pwm2与CAMPCLIN/GPIO46复用,pwm3类似,选择与CAMCLKOUT/GPIO47复用。
所以源码中有
// PWNn所在gpio#define LS1C_PWM0_GPIO06 (6)#define LS1C_PWM1_GPIO92 (92)#define LS1C_PWM2_GPIO46_CAMPCLKIN (46) // 第四复用#define LS1C_PWM3_GPIO47_CAMCLKOUT (47) // 第四复用
源码
应用程序
应用程序通过write()接口写入脉冲个数
test.c
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(void){ int fd = 0; int ret = 0; int pulse_num = 0; fd = open("/dev/ls1c_pwm_pulse", O_RDWR); if (-1 == fd) { printf("[%s] open device file.\n", __FUNCTION__); return -1; } while (1) { pulse_num = 20; ret = write(fd, &pulse_num, sizeof(pulse_num)); if (sizeof(pulse_num) != ret) { close(fd); printf("[%s] write fail. ret=%d\n", __FUNCTION__, ret); return -1; } sleep(1); }}
Makefile
HEADER_FILE = $(wildcard *.h)SRC = $(wildcard *.c)OBJ = $(SRC:.c=.o)DEST = testCC = mipsel-linux-gccall:$(DEST)$(DEST):$(OBJ)$(CC) $^ -o $@cp $@ /nfsramdisk/LS1xrootfs-demo/test$(OBJ):$(SRC) $(HEADER_FILE)$(CC) -c $^clean:rm -f *.o $(DEST)
驱动
/* * drivers\misc\ls1c_pwm_pulse.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/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>enum { LS1C_PWM_0 = 0, LS1C_PWM_1 = 1, LS1C_PWM_2 = 2, LS1C_PWM_3 = 3,};// gpio配置寄存器#define LS1C_GPIO_CFG0 (0xbfd010c0) // 控制gpio[31:0]#define LS1C_GPIO_CFG1 (0xbfd010c4) // 控制gpio[63:32]#define LS1C_GPIO_CFG2 (0xbfd010c8) // 控制gpio[95:64]#define LS1C_GPIO_CFG3 (0xbfd010cc) // 控制gpio[127:96]// 复用寄存器#define LS1C_CBUS_FOURTH1 (0xbfd011f4) // 控制gpio[63:32]的第四复用// PWNn所在gpio#define LS1C_PWM0_GPIO06 (6)#define LS1C_PWM1_GPIO92 (92)#define LS1C_PWM2_GPIO46_CAMPCLKIN (46) // 第四复用#define LS1C_PWM3_GPIO47_CAMCLKOUT (47) // 第四复用// 寄存器偏移#define REG_PWM_CNTR0x00#define REG_PWM_HRC0x04#define REG_PWM_LRC0x08#define REG_PWM_CTRL0x0c// 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_SINGLE (4) // 单脉冲控制位#define LS1C_PWM_OE (3) // 脉冲输出使能控制位#define LS1C_PWM_CNT_EN (0) // CNTR使能位// 脉冲宽度#define PWM_PULSE_HIGH_WIDTH_NS (2*1000) // 高电平2us#define PWM_PULSE_LOW_WIDTH_NS (2*1000) // 低电平2usstatic void __iomem *pwm_pulse_reg_base = NULL; // 映射后的寄存器基地址static unsigned long long pwm_pulse_clk_rate; // pwm计数器的时钟频率static DEFINE_MUTEX(pwm_pulse_lock);// 初始化PWMnstatic void pwm_pulse_PWMn_init(int PWMn){ unsigned long long tmp = 0; unsigned long pulse_high_width_ns = PWM_PULSE_HIGH_WIDTH_NS; unsigned long pulse_low_width_ns = PWM_PULSE_LOW_WIDTH_NS; unsigned int cntr_reg_data = 0; // 写入控制寄存器的数据 unsigned int data = 0; void __iomem *reg_base = NULL; void __iomem *addr = NULL; // 配置gpio引脚为pwm,而非gpio switch (PWMn) { case LS1C_PWM_0: addr = (void *)LS1C_GPIO_CFG0; data = readl(addr); data &= ~(1<<LS1C_PWM0_GPIO06); writel(data, addr); break; case LS1C_PWM_1: addr = (void *)LS1C_GPIO_CFG2; data = readl(addr); data &= ~(1<<(LS1C_PWM1_GPIO92-64)); writel(data, addr); break; case LS1C_PWM_2: addr = (void *)LS1C_GPIO_CFG1; data = readl(addr); data &= ~(1<<(LS1C_PWM2_GPIO46_CAMPCLKIN-32)); writel(data, addr); break; case LS1C_PWM_3: addr = (void *)LS1C_GPIO_CFG1; data = readl(addr); data &= ~(1<<(LS1C_PWM3_GPIO47_CAMCLKOUT-32)); writel(data, addr); break; } // 复用 switch (PWMn) { // pwm0和pwm1不需要复用 case LS1C_PWM_0: case LS1C_PWM_1: break; // pwm2开启第四复用 case LS1C_PWM_2: addr = (void *)LS1C_CBUS_FOURTH1; data = readl(addr); data |= (1<<(LS1C_PWM2_GPIO46_CAMPCLKIN-32)); writel(data, addr); break; case LS1C_PWM_3: addr = (void *)LS1C_CBUS_FOURTH1; data = readl(addr); data |= (1<<(LS1C_PWM3_GPIO47_CAMCLKOUT-32)); writel(data, addr); break; } reg_base = pwm_pulse_reg_base+(PWMn<<4); // 写寄存器HRC tmp = pwm_pulse_clk_rate * pulse_low_width_ns; do_div(tmp, 1000000000); writel(--tmp, reg_base+REG_PWM_HRC); // 写寄存器LRC tmp = pwm_pulse_clk_rate * (pulse_high_width_ns+pulse_low_width_ns); do_div(tmp, 1000000000); writel(--tmp, reg_base+REG_PWM_LRC); // 写主计数器 writel(0, reg_base+REG_PWM_CNTR); // 写控制寄存器 cntr_reg_data = (0 << LS1C_PWM_INT_LRC_EN) | (0 << LS1C_PWM_INT_HRC_EN) | (0 << LS1C_PWM_CNTR_RST) | (0 << LS1C_PWM_INT_SR) | (0 << LS1C_PWM_INTEN) | (1 << LS1C_PWM_SINGLE) | (0 << LS1C_PWM_OE) | (0 << LS1C_PWM_CNT_EN); addr = reg_base+REG_PWM_CTRL; writel(cntr_reg_data, addr); return ;}// 在PWMn引脚上产生一个脉冲static void pwm_pulse_one_pulse(int PWMn){ unsigned int cntr_reg_data = 0; // 写入控制寄存器的数据 void __iomem *reg_base = NULL; reg_base = pwm_pulse_reg_base+(PWMn<<4); // 写主计数器 writel(0, reg_base+REG_PWM_CNTR); // 写控制寄存器 cntr_reg_data = (0 << LS1C_PWM_INT_LRC_EN) | (0 << LS1C_PWM_INT_HRC_EN) | (0 << LS1C_PWM_CNTR_RST) | (0 << LS1C_PWM_INT_SR) | (0 << LS1C_PWM_INTEN) | (1 << LS1C_PWM_SINGLE) | (0 << LS1C_PWM_OE) | (1 << LS1C_PWM_CNT_EN); writel(cntr_reg_data, reg_base+REG_PWM_CTRL); return ;}static int pwm_pulse_open(struct inode *inode, struct file *filp){ return 0;}static int pwm_pulse_close(struct inode *inode, struct file *filp){ return 0;}static ssize_t pwm_pulse_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp){ int ret = 0; unsigned int pulse_num = 0; // 脉冲个数 unsigned tmp; if (mutex_lock_interruptible(&pwm_pulse_lock)) { return -ERESTARTSYS; } ret = copy_from_user(&pulse_num, buf, sizeof(pulse_num)); mutex_unlock(&pwm_pulse_lock); if (ret) { printk(KERN_ERR "[%s] write err. pulse_num=%u\n", __FUNCTION__, pulse_num); return -1; } // 产生指定个数的脉冲 for (tmp=0; tmp<pulse_num; tmp++) { pwm_pulse_one_pulse(LS1C_PWM_0); pwm_pulse_one_pulse(LS1C_PWM_1); pwm_pulse_one_pulse(LS1C_PWM_2); pwm_pulse_one_pulse(LS1C_PWM_3); udelay(10); } return sizeof(pulse_num);}static struct file_operations ls1c_pwm_pulse_ops = { .owner = THIS_MODULE, .open = pwm_pulse_open, .release = pwm_pulse_close, .write = pwm_pulse_write,};static struct miscdevice ls1c_pwm_pulse_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "ls1c_pwm_pulse", .fops = &ls1c_pwm_pulse_ops,};static int pwm_pulse_probe(struct platform_device *pdev){ int ret = 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_pulse_reg_base = ioremap(res->start, resource_size(res)); if (NULL == pwm_pulse_reg_base) { printk(KERN_ERR "[%s] ioremap pwm register fail.\n", __FUNCTION__); ret = -ENODEV; goto fail_free_res; } // 获取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_free_io; } pwm_pulse_clk_rate = (unsigned long long)clk_get_rate(pwm_clk); clk_put(pwm_clk); // 初始化PWMn pwm_pulse_PWMn_init(LS1C_PWM_0); pwm_pulse_PWMn_init(LS1C_PWM_1); pwm_pulse_PWMn_init(LS1C_PWM_2); pwm_pulse_PWMn_init(LS1C_PWM_3); return 0;fail_free_io: iounmap(pwm_pulse_reg_base);fail_free_res: release_mem_region(res->start, resource_size(res)); return ret;}static int pwm_pulse_remove(struct platform_device *pdev){ struct resource *res = NULL; iounmap(pwm_pulse_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_pulse_driver = { .driver = { .name = "ls1c_pwm_pulse", .owner = THIS_MODULE, }, .probe = pwm_pulse_probe, .remove = pwm_pulse_remove,};static int __init pwm_pulse_init(void){ if (misc_register(&ls1c_pwm_pulse_miscdev)) { printk(KERN_ERR "could not register pwm pulse driver!\n"); return -EBUSY; } return platform_driver_register(&ls1c_pwm_pulse_driver);}static void __exit pwm_pulse_exit(void){ misc_deregister(&ls1c_pwm_pulse_miscdev); platform_driver_unregister(&ls1c_pwm_pulse_driver);}module_init(pwm_pulse_init);module_exit(pwm_pulse_exit);MODULE_AUTHOR("勤为本");MODULE_DESCRIPTION("使用ls1c的硬件pwm产生单个脉冲");MODULE_LICENSE("GPL");
在“linux源码根目录\arch\mips\loongson\ls1x\ls1c\platform.c”中加入
#ifdef CONFIG_LS1C_PWM_PULSEstatic struct resource ls1c_pwm_pulse_resources[] = { { .start = LS1X_PWM0_BASE, .end = LS1X_PWM0_BASE + 0x10*4 -1, // pwm0-3 .flags = IORESOURCE_MEM, }};static struct platform_device ls1c_pwm_pulse = { .name = "ls1c_pwm_pulse", .resource = ls1c_pwm_pulse_resources, .num_resources = ARRAY_SIZE(ls1c_pwm_pulse_resources),};#endif // End of CONFIG_LS1C_PWM_PULSE
在变量“static struct platform_device *ls1b_platform_devices[] __initdata”中加入
#ifdef CONFIG_LS1C_PWM_PULSE &ls1c_pwm_pulse,#endif在“linux源码根目录\drivers\misc\Kconfig”中加入
config LS1C_PWM_PULSE tristate "ls1c pwm pulse" depends on LS1C_MACH help Say Y here if you want to build a pwm pulse driver for ls1c
在“linux源码根目录\drivers\misc\Makefile”中加入
obj-$(CONFIG_LS1C_PWM_PULSE) += ls1c_pwm_pulse.o
配置
make menuconfigDevice Drivers --->
[*] Misc devices --->
<*> ls1c pwm pulse
make
运行效果
一次产生20个脉冲,再来看看每个脉冲的详细情况
代码中设置了一个脉冲的高电平和低电平都是2us,如下
// 脉冲宽度#define PWM_PULSE_HIGH_WIDTH_NS (2*1000) // 高电平2us#define PWM_PULSE_LOW_WIDTH_NS (2*1000) // 低电平2usA4988要求脉冲的高低电平至少1us,这里留了点余量,设置为2us。驱动的write函数中,每产生一个脉冲后,就延迟了10us,所以看到以上结果。
测试时,发现pwm0,pwm2,和pwm3都能正常输出单脉冲,唯独pwm1的波形有点异常,高电平没有上升到想要的高度,大约上升到了1v左右,如下
经过仔细查看原理图后,发现pwm1的引脚gpio92上接有一个按键和电容,原理图如下
电容c83是为了按键消抖用的,可是我这里不需要在gpio92(pwm1)上接按键,所以果断把c83用烙铁取下来,如下
测试,一切正常。果然是这个电容影响了。
拓展
如果想产生标准的pwm波形,只需要把pwm的控制寄存器的第4位SINGLE置0就可以了,其它的配置完全一样。1c的linux源码中文件“arch\mips\loongson\ls1x\pwm.c”已经封装了好几个函数,只需要在make menuconfig时,选上
Machine selection --->
[*] Enable PWM
然后就可以直接调用了。
- 【龙印】用龙芯1c的硬件pwm产生单个脉冲来驱动步进电机
- PWM脉冲数控制步进电机
- 定时器3控制PWM的输出脉冲_步进电机的控制
- 步进电机单双脉冲
- 步进电机转速与脉冲频率的关系
- 脉冲频率对步进电机转矩的影响
- 电机驱动的三种脉冲模式
- pwm 控制 步进电机 小车
- 51控制步进电机的驱动
- 28BYJ48步进电机的驱动原理
- 两相四线步进电机的驱动
- 步进电机单双脉冲控制说明
- linux步进电机驱动
- 步进电机linux驱动
- 步进电机细分驱动
- STM32驱动步进电机
- 步进电机驱动基础
- 步进电机驱动芯片
- Java闭关修炼64课 很适合新手学习的JAVA视频教程
- RecyclerView简单使用笔记
- 回车、换行、空格的ASCII码值
- Android N 与Android M InCallUI代码对比(基于CM)
- docker 开发常用命令总结
- 【龙印】用龙芯1c的硬件pwm产生单个脉冲来驱动步进电机
- 关于classpath
- Navicat破解
- JS实现图片不存在时显示默认图片
- C# 调用WebService方法(包含跨服务器校验问题)
- 图像分析-回归线性算法
- 【工具】文件之间查找差异工具
- 1032. 挖掘机技术哪家强(20)
- 尝试配置本地yum源