Kernel configuration settin
/* 1. Add node in device tree root's configuration */
/ {
model = "Nuvoton MA35D1-SOM";
………
gpio_pwm {
compatible = "gpio-pwm";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_pwm>;
gpios = <&gpiog 1 GPIO_ACTIVE_LOW>,
<&gpiog 2 GPIO_ACTIVE_LOW>;
};
………
};
/* 2. Add module pin configuration to device tree's pinctl as below */
&pinctrl {
………
gpio_pwm {
pinctrl_gpio_pwm: gpio_pwmgrp{
nuvoton,pins =
<SYS_GPG_MFPL_PG1MFP_GPIO &pcfg_default>,
<SYS_GPG_MFPL_PG2MFP_GPIO &pcfg_default>;
};
};
………
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/reboot.h>
#include <linux/pwm.h>
#include <linux/gpio.h>
#include <linux/hrtimer.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include "gpio_pwm.h"
#define GPIO_STATE_DEFAULT 0
struct gpio_pwm_driver_data {
unsigned int gpio;
bool gpio_state;
struct pwm_device *pwm_dev;
bool pwm_enable;
bool pwm_requset;
struct hrtimer timer;
struct mutex lock;
};
struct gpio_pwm_chip {
struct pwm_chip chip;
int gpio_nums;
struct gpio_pwm_driver_data gpio_pwm_drv_data[];
};
static enum hrtimer_restart gpio_pwm_timer_fun(struct hrtimer *data)
{
int gpio_pwm_hi_time = 0;
int gpio_pwm_lo_time = 0;
int gpio_keep_time = 0;
struct gpio_pwm_driver_data *gpio_pwm_drv = container_of(data, struct gpio_pwm_driver_data, timer);
struct pwm_device *pwm_dev = gpio_pwm_drv->pwm_dev;
gpio_pwm_hi_time = pwm_dev->state.duty_cycle;
gpio_pwm_lo_time = pwm_dev->state.period - pwm_dev->state.duty_cycle;
if(gpio_pwm_drv->gpio_state == 0) {
gpio_keep_time = gpio_pwm_hi_time;
} else {
gpio_keep_time = gpio_pwm_lo_time;
}
gpio_pwm_drv->gpio_state ^= 0x01;
gpio_set_value(gpio_pwm_drv->gpio, gpio_pwm_drv->gpio_state);
hrtimer_forward_now(&gpio_pwm_drv->timer, ns_to_ktime(gpio_keep_time));
return HRTIMER_RESTART;
}
static int gpio_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct gpio_pwm_chip *pwm_chip_ptr = container_of(chip, struct gpio_pwm_chip, chip);
struct gpio_pwm_driver_data *gpio_pwm_drv = &(pwm_chip_ptr->gpio_pwm_drv_data[pwm->hwpwm]);
int ret = 0;
ret = gpio_request_one(gpio_pwm_drv->gpio, GPIOF_DIR_OUT, pwm->label);
if (ret == 0) {
gpio_pwm_drv->gpio_state = GPIO_STATE_DEFAULT;
gpio_set_value(gpio_pwm_drv->gpio, gpio_pwm_drv->gpio_state);
gpio_pwm_drv->pwm_requset = 1;
}
return ret;
}
static void gpio_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct gpio_pwm_chip *pwm_chip_ptr = container_of(chip, struct gpio_pwm_chip, chip);
struct gpio_pwm_driver_data *gpio_pwm_drv = &(pwm_chip_ptr->gpio_pwm_drv_data[pwm->hwpwm]);
int i;
mutex_lock(&gpio_pwm_drv->lock);
if(gpio_pwm_drv->pwm_enable) {
hrtimer_cancel(&gpio_pwm_drv->timer);
gpio_pwm_drv->pwm_enable = false;
}
mutex_unlock(&gpio_pwm_drv->lock);
for(i=0; i < pwm_chip_ptr->gpio_nums; i++) {
if (gpio_pwm_drv->pwm_requset == 1) {
gpio_free(gpio_pwm_drv->gpio);
}
}
}
static int gpio_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct gpio_pwm_chip *pwm_chip_ptr = container_of(chip, struct gpio_pwm_chip, chip);
struct gpio_pwm_driver_data *gpio_pwm_drv = &(pwm_chip_ptr->gpio_pwm_drv_data[pwm->hwpwm]);
mutex_lock(&gpio_pwm_drv->lock);
if(!gpio_pwm_drv->pwm_enable) {
hrtimer_init(&gpio_pwm_drv->timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
gpio_pwm_drv->timer.function = gpio_pwm_timer_fun;
gpio_pwm_drv->pwm_enable = true;
}
mutex_unlock(&gpio_pwm_drv->lock);
hrtimer_start(&gpio_pwm_drv->timer, ktime_add_ns(ktime_get(), pwm->state.duty_cycle), HRTIMER_MODE_ABS);
return 0;
}
static int gpio_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns)
{
pwm->state.period = period_ns;
pwm->state.duty_cycle = duty_ns;
return 0;
}
static void gpio_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct gpio_pwm_chip *pwm_chip_ptr = container_of(chip, struct gpio_pwm_chip, chip);
struct gpio_pwm_driver_data * gpio_pwm_drv = &(pwm_chip_ptr->gpio_pwm_drv_data[pwm->hwpwm]);
mutex_lock(&gpio_pwm_drv->lock);
if(gpio_pwm_drv->pwm_enable) {
hrtimer_cancel(&gpio_pwm_drv->timer);
gpio_pwm_drv->pwm_enable = false;
}
mutex_unlock(&gpio_pwm_drv->lock);
}
static struct pwm_ops gpio_pwm_ops = {
.request = gpio_pwm_request,
.free = gpio_pwm_free,
.enable = gpio_pwm_enable,
.disable = gpio_pwm_disable,
.config = gpio_pwm_config,
.owner = THIS_MODULE,
};
#ifdef CONFIG_OF
static const struct of_device_id of_gpio_pwm_match[] = {
{ .compatible = "gpio-pwm", },
{},
};
#endif
static struct gpio_pwm_chip *gpio_pwmparse_dt(struct device *dev)
{
const struct of_device_id *of_id = of_match_device(of_gpio_pwm_match, dev);
struct device_node *np = dev->of_node;
enum of_gpio_flags flags;
struct gpio_pwm_chip *pwm_chip_ptr;
int gpio_nums;
int i;
int err;
if (!of_id || !np)
return NULL;
gpio_nums = of_gpio_count(np);
if (!gpio_nums)
return ERR_PTR(-ENODEV);
printk("gpio_nums = %d\n", gpio_nums);
pwm_chip_ptr = devm_kzalloc(dev,
sizeof(struct gpio_pwm_chip) + (gpio_nums * sizeof(struct gpio_pwm_driver_data)), GFP_KERNEL);
if (!pwm_chip_ptr)
return ERR_PTR(-ENOMEM);
pwm_chip_ptr->gpio_nums = gpio_nums;
for(i = 0; i < gpio_nums; i++) {
pwm_chip_ptr->gpio_pwm_drv_data[i].gpio = of_get_gpio_flags(np, i, &flags);
mutex_init(&(pwm_chip_ptr->gpio_pwm_drv_data[i].lock));
pwm_chip_ptr->gpio_pwm_drv_data[i].pwm_enable = false;
printk("gpio=%d\n", pwm_chip_ptr->gpio_pwm_drv_data[i].gpio);
}
err = of_property_read_u32(np, "base", &pwm_chip_ptr->chip.base);
if (err)
pwm_chip_ptr->chip.base = 0x20;
return pwm_chip_ptr;
}
static int gpio_pwm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_pwm_platform_data *pdata = (struct gpio_pwm_platform_data *)dev_get_platdata(dev);
struct gpio_pwm_chip *pwm_chip_ptr = NULL;
int i = 0;
int ret = 0;
if (!pdata) {
printk("probe dt\n");
pwm_chip_ptr = gpio_pwmparse_dt(dev);
if (IS_ERR(pwm_chip_ptr))
return PTR_ERR(pwm_chip_ptr);
if (!pwm_chip_ptr) {
dev_err(dev, "pwm gpio missing platform data\n");
return -EINVAL;
}
} else {
pwm_chip_ptr = devm_kzalloc(&pdev->dev, sizeof(struct gpio_pwm_chip) + (pdata->gpio_nums * sizeof(struct gpio_pwm_driver_data)), GFP_KERNEL);
if(pwm_chip_ptr == NULL)
return -ENOMEM;
pwm_chip_ptr->gpio_nums = pdata->gpio_nums;
for(i=0; i < pdata->gpio_nums; i++) {
pwm_chip_ptr->gpio_pwm_drv_data[i].gpio = pdata->gpios[i];
mutex_init(&(pwm_chip_ptr->gpio_pwm_drv_data[i].lock));
pwm_chip_ptr->gpio_pwm_drv_data[i].pwm_enable = false;
printk("gpio-%d\n",pwm_chip_ptr->gpio_pwm_drv_data[i].gpio);
}
pwm_chip_ptr->chip.base = pdata->pwm_chip_idx;
printk("pwm_chip_idx=%d\n", pdata->pwm_chip_idx);
}
pwm_chip_ptr->chip.npwm = pwm_chip_ptr->gpio_nums;
pwm_chip_ptr->chip.ops = &gpio_pwm_ops;
pwm_chip_ptr->chip.dev = &pdev->dev;
#ifdef CONFIG_OF
pwm_chip_ptr->chip.dev->of_node = pdev->dev.of_node;
#endif
ret = pwmchip_add(&pwm_chip_ptr->chip);
if(ret) {
return ret;
}
for(i = 0; i < pwm_chip_ptr->gpio_nums; i++) {
pwm_chip_ptr->gpio_pwm_drv_data[i].pwm_dev = &pwm_chip_ptr->chip.pwms[i];
}
platform_set_drvdata(pdev, pwm_chip_ptr);
return 0;
}
static int gpio_pwm_remove(struct platform_device *pdev)
{
struct gpio_pwm_chip *pwm_chip_ptr = platform_get_drvdata(pdev);
pwmchip_remove(&pwm_chip_ptr->chip);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver gpio_pwm_driver = {
.probe = gpio_pwm_probe,
.remove = gpio_pwm_remove,
.driver = {
.name = "gpio-pwm",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_gpio_pwm_match),
},
};
module_platform_driver(gpio_pwm_driver);
MODULE_AUTHOR("nuvoton porting");
MODULE_DESCRIPTION("gpio pwm chip driver");
MODULE_LICENSE("GPL");
#ifndef __GPIO_PWM_H
#define __GPIO_PWM_H
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
struct gpio_pwm_platform_data {
int pwm_chip_idx;
int gpio_nums;
unsigned int *gpios;
};
#endif
下面要注意 :
PWD := $(shell pwd)
obj-m += gpio-pwm.o
.PHONY: all clean
COMPILE=aarch64-poky-linux-
LINUX_DIR=../tmp-glibc/work/numaker_som_ma35d16a81-poky-linux/linux-ma35d1/5.10.140-r0/build
all:
make ARCH=arm64 CROSS_COMPILE=$(COMPILE) -C $(LINUX_DIR) M=$(PWD) modules
GPIO PWM執行輸出與佔空比設定
Export PWM
echo 0 > /sys/class/pwm/pwmchip32/export
PWM 週期設定,單位為ns, gpioi12 輸出頻率為 100 HZ
echo 10000000 > /sys/class/pwm/pwmchip32/pwm0/period
PWM 佔空比設定, 以下設定佔空比為 0.5
echo 5000000 > /sys/class/pwm/pwmchip32/pwm0/duty_cycle
PWM 使能輸出
echo 1 >/sys/class/pwm/pwmchip32/pwm0/enable
echo 0 > /sys/class/pwm/pwmchip32/export
echo 20000000 > /sys/class/pwm/pwmchip32/pwm0/period
echo 1000000 > /sys/class/pwm/pwmchip32/pwm0/duty_cycle
echo 2000000 > /sys/class/pwm/pwmchip32/pwm0/duty_cycle
echo 1 >/sys/class/pwm/pwmchip32/pwm0/enable
echo 1 export ,這個時候會出現pwm1的節點
echo 1 > /sys/class/pwm/pwmchip32/export
echo 20000000 > /sys/class/pwm/pwmchip32/pwm1/period
echo 1000000 > /sys/class/pwm/pwmchip32/pwm1/duty_cycle
echo 2000000 > /sys/class/pwm/pwmchip32/pwm1/duty_cycle
echo 1 >/sys/class/pwm/pwmchip32/pwm1/enable
小巧輕便且輸出功率高。伺服馬達可以大約旋轉180度(每個方向90度),並且與標準的伺服馬達一樣工作,只是體積更小。
您可以使用任何伺服馬達的程式碼、硬體或庫來控制這些伺服馬達。
對於初學者來說非常適合,他們想要讓東西移動,而不需要建立帶有反饋和齒輪箱的馬達控制器,尤其是因為它可以放在狹小的空間中。
位置 “0”(1.5 毫秒脈衝)代表中間位置。
位置 “90”(約 2 毫秒脈衝)代表向右極限。
位置 “-90”(約 1 毫秒脈衝)代表向左極限。
簡單來說,這段描述了 PWM 信號的位置與對應的角度之間的關係。位置 “0” 代表中間位置,位置 “90” 代表完全向右轉的極限,而位置 “-90” 則代表完全向左轉的極限。這些位置對應於 PWM 脈衝的持續時間,可以控制舵機或其他類似裝置的運動位置。
100 Hz 是 :
echo 10000000 > /sys/class/pwm/pwmchip32/pwm0/period
Datashet 要 50 Hz
要寫成以下才會是50 Hz:
1 ms 的duty 我要給以下:
echo 1000000 > /sys/class/pwm/pwmchip32/pwm0/duty_cycle
1.5 ms 的duty 我要給以下:
echo 1500000 > /sys/class/pwm/pwmchip32/pwm0/duty_cycle
2 ms 的duty 我要給以下:
echo 2000000 > /sys/class/pwm/pwmchip32/pwm0/duty_cycle