安裝步驟文件

MA35D1 Control

MA35D1 Control

要找MA35D1的兩根pin腳給PWM

blog圖片的連結

新唐定義 PG1 PG2 的地方

blog圖片的連結

設定 device tree

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>;
		};		
	};
………

gpio-pwm 的Driver

gpio-pwm.c

#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");

gpio-pwm.h

#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

MakeFile to Build gpio-pwm.ko

下面要注意 :

  1. 在docker裡面 make
  2. LINUX_DIR 要擺已經Build過的 kernel 位置
  3. aarch64 tool chain 要有 : source /usr/local/oecore-x86_64/environment-setup-aarch64-poky-linux
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
	
	

結構應該長得像下面的資料夾

blog圖片的連結

實體接線

blog圖片的連結 blog圖片的連結

AP 層控制

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 會 export ,這個時候,會出現pwm0的節點
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
PWM1 也是差不多的方式

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

SG90 伺服馬達說明

小巧輕便且輸出功率高。伺服馬達可以大約旋轉180度(每個方向90度),並且與標準的伺服馬達一樣工作,只是體積更小。

您可以使用任何伺服馬達的程式碼、硬體或庫來控制這些伺服馬達。

對於初學者來說非常適合,他們想要讓東西移動,而不需要建立帶有反饋和齒輪箱的馬達控制器,尤其是因為它可以放在狹小的空間中。

位置 “0”(1.5 毫秒脈衝)代表中間位置。

位置 “90”(約 2 毫秒脈衝)代表向右極限。

位置 “-90”(約 1 毫秒脈衝)代表向左極限。

簡單來說,這段描述了 PWM 信號的位置與對應的角度之間的關係。位置 “0” 代表中間位置,位置 “90” 代表完全向右轉的極限,而位置 “-90” 則代表完全向左轉的極限。這些位置對應於 PWM 脈衝的持續時間,可以控制舵機或其他類似裝置的運動位置。

blog圖片的連結

注意

100 Hz 是 :

echo 10000000 > /sys/class/pwm/pwmchip32/pwm0/period

Datashet 要 50 Hz

要寫成以下才會是50 Hz:

echo 20000000 > /sys/class/pwm/pwmchip32/pwm0/period

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

comments powered by Disqus