博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【龙印】用龙芯1c的硬件pwm产生单个脉冲来驱动步进电机
阅读量:2359 次
发布时间:2019-05-10

本文共 8528 字,大约阅读时间需要 28 分钟。

本文为在用龙芯1c做3D打印机过程中的笔记。龙芯1c做的3d打印机简称“龙印”,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 
#include
#include
#include
#include
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 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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_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_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_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
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 menuconfig
  Device 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)    // 低电平2us
A4988要求脉冲的高低电平至少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
然后就可以直接调用了。

你可能感兴趣的文章
LSP注入概述
查看>>
网络封包过滤之分层服务提供者(LSP)
查看>>
linux 部署多个tomcat
查看>>
vue用i18n实现多语言支持(国际化)
查看>>
搭建性能比Squid高很多的Varnish服务器
查看>>
Varnish - 高效的 HTTP accelerator
查看>>
Linux索引节点(inode)用满导致的一次故障
查看>>
DenyHosts 阻止SSH暴力破解最好的方法
查看>>
Linux必学60个命令文件处理
查看>>
Securing a Default Install of RedHat Linux 8.0/9.0
查看>>
Linux系统下应用知识大荟萃(一)
查看>>
深入理解linux启动过程
查看>>
locale 详解(转载)
查看>>
linux系统主机安全配置!
查看>>
说说大型高并发高负载网站的系统架构
查看>>
大型Web2.0站点构建技术初探
查看>>
使用开源软件,设计高性能可扩展互动网站
查看>>
高并发高流量网站架构
查看>>
再说 Linux 下恢复 EXT3 Superblock
查看>>
YouTube 的架构扩展
查看>>