[Desarrollo y aprendizaje del controlador IMX6ULL] 21. Subsistema PWM impulsado por Linux (tome el mecanismo de dirección SG90 como ejemplo)

1. Sección del árbol de dispositivos

En primer lugar, se han definido algunos nodos del árbol de dispositivos pwm en el archivo imx6ull.dtsi , aquí tomamos pwm2 como ejemplo

pwm2: pwm@02084000 {
    
    
	compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
	reg = <0x02084000 0x4000>;
	interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_DUMMY>,
		 <&clks IMX6UL_CLK_DUMMY>;
	clock-names = "ipg", "per";
	#pwm-cells = <2>;
};

Necesitamos hacer referencia y habilitar este nodo en el archivo de árbol de dispositivos (.dts) y, al mismo tiempo, especificar el pin GPIO al que se asigna el pwm (es decir, el subsistema pinctrl, que asigné a GPIO1_9 aquí)

&iomuxc {
    
    
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
    
    
		......
		......

		/* SG90 PWM2 GPIO1_IO09 */
		pinctrl_pwm2: pwm2grp {
    
    
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO09__PWM2_OUT   0x110b0
			>;
		};
		......
		......
}

......
......

&pwm2 {
    
    
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_pwm2>;
	clocks = <&clks IMX6UL_CLK_PWM2>,
			 <&clks IMX6UL_CLK_PWM2>;
	status = "okay";
};

Para usar pwm, solo necesita agregar dos piezas de información de atributos en el nodo del árbol de dispositivos, como se muestra a continuación

pwms = <&PWMn id period_ns>;
pwm-names = "name";
  • pwms: el atributo es obligatorio, tiene tres valores de atributo

  • &PWMn especifica qué pwm usar, definido en el archivo imx6ull.dtsi, hay 8 opciones en total;

  • id: la identificación de pwm generalmente se establece en 0.

  • period_ns: se utiliza para establecer el período. La unidad es ns.

  • pwm-names: define el nombre del dispositivo pwm. (no se puede configurar)

Finalmente, agregue su propio nodo definido debajo del nodo raíz

hc_sg90 {
    
    
	compatible    =  "hc-sg90";
	pwms = <&pwm2 0 20000000>;    /* 使用pwm1  id为0   周期为20000000ns = 20ms */
	status 		  =  "okay";
};

2. Parte del código del controlador

El antiguo marco de controlador de dispositivo de caracteres:

  • conducir entrada salida
  • La entrada del controlador define el registro de dispositivos de caracteres, la creación de nodos de dispositivos de caracteres y el registro de dispositivos de plataforma;
  • Exportación del controlador anular el registro del dispositivo de plataforma, eliminar el nodo del dispositivo de caracteres, anular el registro del dispositivo de caracteres
  • Construya la estructura file_operations
  • Cree la estructura de dispositivo_plataforma y escriba la función de sondeo

Como se muestra en el siguiente código:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/timekeeping.h>
#include <linux/wait.h>
#include <linux/irqflags.h>
#include <linux/pwm.h>

static int major;
static struct class *class;

static struct pwm_device *pwm_test;

static int sg90_probe(struct platform_device *pdev)
{
    
    
    struct device_node *node = pdev->dev.of_node;

    printk("sg90 match success \n");
    if (node){
    
    
        /* 从子节点中获取PWM设备 */
        pwm_test = devm_of_pwm_get(&pdev->dev, node, NULL);  
        if (IS_ERR(pwm_test)){
    
    
            printk(KERN_ERR" pwm_test,get pwm  error!!\n");
            return -1;
        }
    }
    else{
    
    
        printk(KERN_ERR" pwm_test of_get_next_child  error!!\n");
        return -1;
    }

    pwm_config(pwm_test, 1500000, 20000000);   /* 配置PWM:1.5ms,90度,周期:20000000ns = 20ms */
    pwm_set_polarity(pwm_test, PWM_POLARITY_NORMAL); /* 设置输出极性:占空比为高电平 */
    pwm_enable(pwm_test);    /* 使能PWM输出 */

    return 0;
}

static int sg90_remove(struct platform_device *dev)
{
    
    
	pwm_config(pwm_test, 500000, 20000000);  /* 配置PWM:0.5ms,0度 */
	pwm_free(pwm_test);

	return 0;
}

static const struct of_device_id sg90_of_match[] = {
    
    
	{
    
     .compatible = "hc-sg90" },
	{
    
     }
};

static struct platform_driver sg90_platform_driver = {
    
    
	.driver = {
    
    
		.name		= "my_sg90",
		.of_match_table	= sg90_of_match,
	},
	.probe			= sg90_probe,
	.remove			= sg90_remove,
};


static int sg90_open (struct inode *node, struct file *filp)
{
    
    
	return 0;
}

static ssize_t sg90_write (struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    
    
	int res;
	unsigned char data[1];
	if(size != 1)
		return 1;

	res = copy_from_user(data, buf, size);
	/* 配置PWM:旋转任意角度(单位1度) */
	pwm_config(pwm_test, 500000 + data[0] * 100000 / 9, 20000000);   
	return 1;
}

static int sg90_release (struct inode *node, struct file *filp)
{
    
    
	return 0;
}


static struct file_operations sg90_ops = {
    
    
	.owner		=	THIS_MODULE,
	.open 		= 	sg90_open,
	.write 		= 	sg90_write,
	.release 	=	sg90_release,
};

static int sg90_init(void)
{
    
    
	major = register_chrdev(0 , "sg90", &sg90_ops);
	class = class_create(THIS_MODULE, "sg90_class");
	device_create(class, NULL, MKDEV(major, 0), NULL, "sg90");

	platform_driver_register(&sg90_platform_driver);
	
	return 0;
}

static void sg90_exit(void)
{
    
    
	platform_driver_unregister(&sg90_platform_driver);
	
	device_destroy(class, MKDEV(major, 0));
	class_destroy(class);
	unregister_chrdev(major, "sg90");
}

module_init(sg90_init);
module_exit(sg90_exit);
MODULE_LICENSE("GPL");
  • Primero, struct device_node *node = pdev->dev.of_node; para obtener los nodos secundarios. En el complemento del árbol de dispositivos, guardamos la información relacionada con PWM en los nodos secundarios de hc_sg90 , por lo que aquí obtenemos los nodos secundarios primero.

  • Después de que el nodo secundario se obtiene con éxito, usamos la función devm_of_pwm_get para obtener pwm. Dado que solo hay un PWM en el nodo, el último parámetro se establece directamente en NULL, de modo que obtendrá el primer PWM.

  • Llame a las funciones pwm_config, pwm_set_polarity y pwm_enable para configurar **PWM, establecer la polaridad de salida y habilitar la salida PWM **Cabe señalar que la polaridad configurada aquí es la polaridad normal, por lo que el segundo parámetro de la función pwm_config es configurado en pwm Un evento alto dentro de un ciclo de la ola.

Entre ellos, el cálculo del ciclo de trabajo de SG90 en la función de escritura no es mucho que decir, se calcula de acuerdo con la siguiente figura

inserte la descripción de la imagen aquí
No es difícil sacar la conclusión de que cada 1ms (1000000ns) del tiempo de alto nivel corresponde a un ángulo de 9 grados más
Cuando el ángulo se gira a 1 grado, el tiempo de alto nivel correspondiente es (500000 + 1000000)/9 ns (porque 0 grados corresponde a El tiempo de alto nivel es 0.5ms = 500000ns)
, luego cuando gira a un ángulo de n grados, el tiempo de alto nivel es (500000 + n * 1000000)/9 ns


3. Sección de aplicación

Ejemplo de ejecución: ./sg90_test 90 , es decir, gire a la posición de 90 grados

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    
    
	int fd;
	int res;
	unsigned char buf[1];

	fd = open("/dev/sg90", O_WRONLY);

	if(fd < 0)
	{
    
    
		printf("sg90 open failed\n");
		return 0;
	}

	buf[0] = atoi(argv[1]);
	write(fd, buf, 1);

	close(fd);
	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/HuangChen666/article/details/132041449
Recomendado
Clasificación