这周一直趁着工作闲暇时间,缓慢推进龙芯2k0300蜂鸟板的评测工作,现在总结一下。
一. PWM设备
通过厂商提供的用户手册可知龙芯2k0300 SOC片内总共有4个pwm控制器,并且在对应linux内核的设备树中导出了,所以用户直接使用即可。如图1和图2所示。
图1 设备树中PWM引脚相关定义
二.PWM操作
接下来便可以用最简单的sysfs方式对PWM进行操作了。如下图3所示,可以看出龙芯2k0300 SOC片内总共有4个PWM控制器,分别对应的pwmchip0~pwmchip3。
图3 PWM控制器对应的设备文件路径
进入任意一个PWM控制器设备可以查看其对应的属性文件,如图4所示。
图4 PWM3设备下的属性文件
对用户而言,重点关注export、npwm和unexport三个文件即可。
1.export:顾名思义就是输出,即将当前设备输出。
2.npwm:表示当前PWM设备共有几路通道。如下图5所示,通过命令查看龙芯2k0300 SOC片内PWM3只有1路PWM通道。
图5 PWM3设备下对应1路通道。
3.unxport:搭配export使用,即将之前导出的设备删除掉。
继续查看导出的PWM3控制器对应的通道有什么属性文件了(因为只有1路通道,所以默认是以0作标识的,即PWM0),如图6所示。
可以看到导出的PWM3控制器对应的PWM0通道总共有7个属性文件。如果操作过pwm控制器,尤其是裸机下或者stm32中的pwm控制器,想必这些名字会给人一种似曾相识的感觉,估计不用过多解释就知道应该操作那几个文件,以及如何操作了:通常就是先使能时钟并设置引脚(这里龙芯2k0300 SOC已经默认设置好了),所以只需设置周期、占空比,再使能输出即可,当不用时,去使能即可)。
1.enable:使能对应通道:“0”禁止,1“使能”。
2.period:设置pwm的周期。注意这里是以ns来作计数单位的。
3.duty_cycle:设置pwm的占空比。注意这里是以ns来作计数单位的。
4.polarity:设置pwm极性。通过示波器可以查看下信号。
具体操作可以如下图7所示。尝试写入1000000000ns(1秒)周期和500000000(0.5秒)占空比,成功让对应的LED灯进行了0.5秒亮和0.5秒灭的操作。
图7 通过pwm通道输出信号。
三. 编制对应的PWM驱动文件
有了以上的测试,接下来便可以将其编制成PWM驱动文件以供后续使用。
1.工程文件的组成,如图8所示。
图8 工程的组成
2.PWM.H
#ifndef __PWM_H_
#define __PWM_H_
int pwm_init(int channel);
int pwm_deinit(int channel);
int pwm_period(int channel, const char* period);
int pwm_duty(int channel, const char* duty);
int pwm_polarity(int channel, const int polarity);
int pwm_enable(int channel);
int pwm_disable(int channel);
#endif
3.PWM.C
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "include/pwm.h"
int pwm_init(int channel)
{
int fd;
switch(channel)
{
case 0:
fd = open("/sys/class/pwm/pwmchip0/export", O_WRONLY);
if(fd < 0)
return 1;
write(fd, "0", 1);
fd = open("/sys/class/pwm/pwmchip0/pwm0/enable", O_WRONLY);
if(fd < 0)
return 2;
write(fd, "0", 1);
break;
case 1:
fd = open("/sys/class/pwm/pwnchip1/export", O_WRONLY);
if(fd < 0)
return 3;
write(fd, "0", 1);
fd = open("/sys/class/pwm/pwmchip1/pwm0/enable", O_WRONLY);
if(fd < 0)
return 4;
write(fd, "0", 1);
break;
case 2:
fd = open("/sys/class/pwm/pwmchip2/export", O_WRONLY);
if(fd < 0)
return 5;
write(fd, "0", 1);
fd = open("/sys/class/pwm/pwmchip2/pwm0/enable", O_WRONLY);
if(fd < 0)
return 6;
write(fd, "0", 1);
break;
case 3:
fd = open("/sys/class/pwm/pwmchip3/export", O_WRONLY);
if(fd < 0)
return 7;
write(fd, "0", 1);
fd = open("/sys/class/pwm/pwmchip3/pwm0/enable", O_WRONLY);
if(fd < 0)
return 8;
write(fd, "0", 1);
break;
default:
break;
}
close(fd);
return 0;
}
int pwm_deinit(int channel)
{
int fd;
switch(channel)
{
case 0:
fd = open("/sys/class/pwm/pwmchip0/unexport", O_WRONLY);
if(fd < 0)
return 21;
write(fd, "0", 1);
break;
case 1:
fd = open("/sys/class/pwm/pwmchip1/unexport", O_WRONLY);
if(fd < 0)
return 22;
write(fd, "0", 1);
break;
case 2:
fd = open("/sys/class/pwm/pwmchip2/unexport", O_WRONLY);
if(fd < 0)
return 23;
write(fd, "0", 1);
break;
case 3:
fd = open("/sys/class/pwm/pwmchip3/unexport", O_WRONLY);
if(fd < 0)
return 24;
write(fd, "0", 1);
break;
default:
break;
}
close(fd);
return 0;
}
int pwm_period(int channel, const char* period)
{
int fd;
switch(channel)
{
case 0:
fd = open("/sys/class/pwm/pwmchip0/pwm0/period", O_WRONLY);
if(fd < 0)
return 31;
write(fd, period, sizeof(period));
break;
case 1:
fd = open("/sys/class/pwm/pwmchip1/pwm0/period", O_WRONLY);
if(fd < 0)
return 32;
write(fd, period, sizeof(period));
break;
case 2:
fd = open("/sys/class/pwm/pwmchip2/pwm0/period", O_WRONLY);
if(fd < 0)
return 33;
write(fd, period, sizeof(period));
break;
case 3:
fd = open("/sys/class/pwm/pwmchip3/pwm0/period", O_WRONLY);
if(fd < 0)
return 34;
write(fd, period, sizeof(period));
break;
default:
break;
}
close(fd);
return 0;
}
int pwm_duty(int channel, const char* duty)
{
int fd;
switch(channel)
{
case 0:
fd = open("/sys/class/pwm/pwmchip0/pwm0/duty_cycle", O_WRONLY);
if(fd < 0)
return 41;
write(fd, duty, sizeof(duty));
break;
case 1:
fd = open("/sys/class/pwm/pwmchip1/pwm0/duty_cycle", O_WRONLY);
if(fd < 0)
return 42;
write(fd, duty, sizeof(duty));
break;
case 2:
fd = open("/sys/class/pwm/pwmchip2/pwm0/duty_cycle", O_WRONLY);
if(fd < 0)
return 43;
write(fd, duty, sizeof(duty));
break;
case 3:
fd = open("/sys/class/pwm/pwmchip3/pwm0/duty_cycle", O_WRONLY);
if(fd < 0)
return 44;
write(fd, duty, sizeof(duty));
break;
default:
break;
}
close(fd);
return 0;
}
int pwm_polarity(int channel, const int polarity)
{
int fd;
switch(channel)
{
case 0:
fd = open("/sys/class/pwm/pwmchip0/pwm0/polarity", O_WRONLY);
if(fd < 0)
return 51;
switch(polarity)
{
case 0:
write(fd, "normal", sizeof("normal"));
break;
case 1:
write(fd, "inversed", sizeof("inversed"));
break;
default:
break;
}
break;
case 1:
fd = open("/sys/class/pwm/pwmchip1/pwm0/polarity", O_WRONLY);
if(fd < 0)
return 52;
switch(polarity)
{
case 0:
write(fd, "normal", sizeof("normal"));
break;
case 1:
write(fd, "inversed", sizeof("inversed"));
break;
default:
break;
}
break;
case 2:
fd = open("/sys/class/pwm/pwmchip2/pwm0/polarity", O_WRONLY);
if(fd < 0)
return 53;
switch(polarity)
{
case 0:
write(fd, "normal", sizeof("normal"));
break;
case 1:
write(fd, "inversed", sizeof("inversed"));
break;
default:
break;
}
break;
case 3:
fd = open("/sys/class/pwm/pwmchip3/pwm0/polarity", O_WRONLY);
if(fd < 0)
return 54;
switch(polarity)
{
case 0:
write(fd, "normal", sizeof("normal"));
break;
case 1:
write(fd, "inversed", sizeof("inversed"));
break;
default:
break;
}
break;
default:
break;
}
close(fd);
return 0;
}
int pwm_enable(int channel)
{
int fd;
switch(channel)
{
case 0:
fd = open("/sys/class/pwm/pwmchip0/pwm0/enable", O_WRONLY);
if(fd < 0)
return 61;
write(fd, "1", 1);
break;
case 1:
fd = open("/sys/class/pwm/pwmchip1/pwm0/enable", O_WRONLY);
if(fd < 0)
return 62;
write(fd, "1", 1);
break;
case 2:
fd = open("/sys/class/pwm/pwmchip2/pwm0/enable", O_WRONLY);
if(fd < 0)
return 63;
write(fd, "1", 1);
break;
case 3:
fd = open("/sys/class/pwm/pwmchip3/pwm0/enable", O_WRONLY);
if(fd < 0)
return 64;
write(fd, "1", 1);
break;
default:
break;
}
close(fd);
return 0;
}
int pwm_disable(int channel)
{
int fd;
switch(channel)
{
case 0:
fd = open("/sys/class/pwm/pwmchip0/pwm0/enable", O_WRONLY);
if(fd < 0)
return 71;
write(fd, "0", 1);
break;
case 1:
fd = open("/sys/class/pwm/pwmchip1/pwm0/enable", O_WRONLY);
if(fd < 0)
return 72;
write(fd, "0", 1);
break;
case 2:
fd = open("/sys/class/pwm/pwmchip2/pwm0/enable", O_WRONLY);
if(fd < 0)
return 73;
write(fd, "0", 1);
break;
case 3:
fd = open("/sys/class/pwm/pwmchip3/pwm0/enable", O_WRONLY);
if(fd < 0)
return 74;
write(fd, "0", 1);
break;
default:
break;
}
close(fd);
return 0;
}
4.main.c:相对简单的用来测试下pwm通道。
#include <stdio.h>
#include <unistd.h>
#include "include/pwm.h"
int main(int argc, char *argv[])
{
int channel , polarity;
char* period;
char* duty ;
printf("This is pwm demo\\\\n");
while(1){
printf("Please input the channel: (0 ~ 3)\\\\n");
scanf("%d", &channel);
printf("Please input the period: \\\\n");
scanf("%s", &period);
printf("Please input the duty: \\\\n");
scanf("%s", &duty);
printf("Please input the nomarl(0) or polarity(1): \\\\n");
scanf("%d", &polarity);
pwm_init(channel);
pwm_period(channel, period);
pwm_duty(channel, duty);
pwm_polarity(channel, polarity);
pwm_enable(channel);
getchar();
}
return 0;
}
5.Makefile文件
Target = pwm_demo
ARCH = loongarch
CC = loongarch64-linux-gnu-gcc
build_dir = build_$(ARCH)
src_dir = source
inc_dir = include .
source = $(foreach dir,$(src_dir),$(wildcard $(dir)/*.c))
object = $(patsubst %.c,$(build_dir)/%.o,$(notdir $(source)))
include = $(foreach dir,$(inc_dir),$(wildcard $(dir)/*.h))
CFLAGS = $(patsubst %, -I%, $(inc_dir))
$(build_dir)/$(Target) : $(object) | create_build
$(CC) $^ -o $@
$(build_dir)/%.o : $(src_dir)/%.c $(include) | create_build
$(CC) -c $(CFLAGS) $< -o $@
.PHONY:clean cleanall check create_build
clean:
rm -rf $(build_dir)
cleanall:
rm -rf build_x86 build_arm
check:
[url=home.php?mod=space&uid=70594]@echo[/url] $(CFLAGS)
@echo $(CURDIR)
@echo $(src_dir)
@echo $(source)
@echo $(object)
create_build:
[url=home.php?mod=space&uid=2293869]@MKDIR[/url] -p $(build_dir)
5.测试效果如下视频所示。