[Notas de Linux] notas del controlador LED fáciles de entender

Prólogo

En el último artículo, compartimos el marco del controlador del dispositivo de caracteres . En ese momento, compartimos el controlador hola. Para aprender STM32, comenzamos con la iluminación. Al aprender el controlador de Linux, naturalmente también necesitamos encender una lámpara para jugar. Trate de extraer conocimiento de estas rutinas básicas, de grano fino, de grano fino, y sentar las bases para un conocimiento más complejo más adelante.

Controlador LED independiente del hardware

Recordando el controlador hola, escribimos y leemos cadenas de acuerdo con las necesidades reales. Aquí, por supuesto, también debemos pensar en nuestro controlador LED de acuerdo con la situación real. Cuando el STM32 se ilumina, generalmente emite un nivel bajo para iluminar y un nivel alto para apagar la luz. En el caso del sistema operativo Linux incorporado, naturalmente tenemos que pensar en la idea de escribir 1/0. Analogía a nuestro anterior programa de saludo:

====== 001

Los datos que se escribirán en nuestro programa LED son 0/1 para encender y apagar el LED. Aquí hacemos experimentos con LED en el laboratorio que no tienen nada que ver con el hardware: nuestro controlador imprime cuando led onrecibe el 0 enviado por la aplicación e imprime cuando recibe 1 led off. Imitando el programa hello en el artículo anterior, el programa LED (parte central) que modificamos y que no tiene nada que ver con el hardware es el siguiente:

Aplicación LED:

====== 002

Controlador LED:

====== 003_1

====== 003

Cargue el módulo del controlador LED y ejecute el programa de aplicación:

====== 004

Controlador LED relacionado con hardware

La sección anterior compartió experimentos de controladores de LED que no tienen nada que ver con el hardware, principalmente para aclarar la idea general del controlador de LED. Aquí agregaremos operaciones relacionadas con el hardware para construir controladores LED relacionados con el hardware.

Cuando estamos programando STM32 bare metal, configurar algunos periféricos es en realidad el proceso de operar algunas direcciones. Estas direcciones de periféricos se pueden ver en el manual del chip:

====== 005

Este es un mapa de direcciones. Aquí solo se enumeran los periféricos 边界地址. Cada periférico tiene muchos registros. Las direcciones de estos registros se obtienen compensando la dirección de la base periférica. Del mismo modo, para el chip IMX6ULL de NXP, también hay una dirección similar a esta:

====== 006

En este momento tenemos que escribir el controlador LED bajo el sistema Linux. No son estas direcciones (direcciones físicas) las que se operan en lo que respecta a la operación del hardware, sino las direcciones (direcciones virtuales) proporcionadas por el sistema operativo.

El sistema operativo genera una dirección virtual para nosotros basada en la dirección física, y nuestro controlador LED controla esta dirección indirectamente para controlar la dirección física. En cuanto a cómo están conectadas estas dos direcciones, no ampliaremos el principio.

Desde una perspectiva funcional, el núcleo nos proporciona la función ioremap, que puede asignar direcciones físicas a direcciones virtuales. Esta función está en el archivo del kernel arch/arm/include/asm/io.h:

void __iomem *ioremap(resource_size_t res_cookie, size_t size);
  • res_cookie: la dirección de inicio física a la que se asignará.
  • tamaño: el tamaño del espacio de memoria que se va a asignar.
  • Valor de retorno: apunte a la primera dirección del espacio virtual asignado.

La ioremapfunción correspondiente a la función es:

void iounmap (volatile void __iomem *addr)
  • addr: la primera dirección del espacio de direcciones virtual que no se asignará.

Una vez completada la asignación de direcciones, podemos acceder directamente a la dirección virtual a través del puntero, como por ejemplo:

*GPIO5_DR &= ~(1 << 3);  /* GPIO5_IO03输出低电平 */
*GPIO5_DR |= (1 << 3);   /* GPIO5_IO03输出高电平 */

Aquí hay una breve introducción i.MX 6ULLa GPIO. Con el i.MX 6ULLfin de dar comandos a los puertos IO (grupos) con números, GPIO5 es el quinto grupo, por lo que GPIO5_IO03 es el tercer pin del quinto grupo de puertos. El STM32 usa letras mayúsculas para representar el puerto (grupo), como PA3 representa el tercer pin del puerto A.

i.MX 6ULLHay 5 grupos de GPIO (GPIO1 ~ GPIO5), cada grupo tiene un máximo de 32 pines:

GPIO1 有 32 个引脚: GPIO1_IO0~GPIO1_IO31;
GPIO2 有 22 个引脚: GPIO2_IO0~GPIO2_IO21;
GPIO3 有 29 个引脚: GPIO3_IO0~GPIO3_IO28;
GPIO4 有 29 个引脚: GPIO4_IO0~GPIO4_IO28;
GPIO5 有 12 个引脚: GPIO5_IO0~GPIO5_IO11;

Una vez completada la asignación de direcciones, no solo podemos acceder a la dirección virtual a través del puntero, sino también usar algunas funciones de lectura y escritura que nos proporciona el núcleo:

/* 写操作函数 */
void writeb(u8 value, volatile void __iomem *addr);
void writew(u16 value, volatile void __iomem *addr);
void writel(u32 value, volatile void __iomem *addr);
/* 读操作函数 */
u8 readb(const volatile void __iomem *addr);
u16 readw(const volatile void __iomem *addr);
u32 readl(const volatile void __iomem *addr);

Las tres funciones writeb, writew y writel corresponden a operaciones de escritura de 8 bits, 16 bits y 32 bits respectivamente. El valor del parámetro es el valor a escribir y addr es la dirección a escribir.

Las tres funciones readb, readw y readl corresponden a operaciones de lectura de 8 bits, 16 bits y 32 bits, respectivamente. El parámetro addr es leer la dirección de memoria de escritura y el valor de retorno son los datos leídos.

En este punto podemos modificar la led_initfunción en la sección anterior led_drv_write:

====== 007

====== 008

Al igual que con STM32, para los i.MX 6ULLperiféricos GPIO, hay muchos registros:

====== 009

Arriba solo encendemos una luz, ¿qué pasa si queremos encender varias luces? Entonces tienes que controlar múltiples GPIO. Si la asignación de direcciones se escribe como se indica arriba, el código se verá hinchado. Recuerde que nuestro periférico GPIO STM32 gestiona sus registros a través de una estructura:

====== 010

El __IO aquí es una macro, que representa la palabra clave del lenguaje C. volatilePara evitar que el compilador optimice algunas de nuestras operaciones de hardware, no se puede obtener el resultado deseado. Por ejemplo:

/* 假设REG为寄存器的地址 */
uint32 *REG;
*REG = 0/* 点灯 */
*REG = 1/* 灭灯 */

En este momento, si el REG no se volatilemodifica, la operación de iluminación se optimizará y solo se realizará la operación de iluminación.

Aquí, también podemos imitar STM32 i.MX 6ULLy administrar registros GPIO con una estructura , como:

struct GPIO_RegDef
{
  volatile unsigned int DR;
  volatile unsigned int GDIR;
  volatile unsigned int PSR;
  volatile unsigned int ICR1;
  volatile unsigned int ICR2;
  volatile unsigned int IMR;
  volatile unsigned int ISR;
  volatile unsigned int EDGE_SEL;
};

El orden de los miembros en la estructura está en un orden específico:

====== 011

Debido a que todos estos registros están desplazados de la dirección base del periférico GPIO, como por ejemplo:

====== 012

El pedido no se puede barajar, de lo contrario no se puede acceder correctamente al registro correspondiente. Después de ser administrado con una estructura, podemos mapear de una manera similar a la siguiente:

struct GPIO_RegDef *GPIO5 = ioremap(0x20AC000, sizeof(struct GPIO_RegDef));

Luego puede manipular registros GPIO como STM32, como:

GPIO5->DR &= ~(1 << 3);  /* GPIO5_IO03输出低电平 */
GPIO5->DR |= (1 << 3);   /* GPIO5_IO03输出高电平 */

Controlador LED relacionado con el hardware (versión actualizada)

El controlador LED que compartimos en la sección anterior es un controlador LED convencional que solo se puede aplicar a nuestra versión de desarrollo actual, por lo que es un controlador LED dedicado. Si cambia otra placa, el pin gpio conectado al LED puede ser diferente. Modificaremos las led_drv.coperaciones relacionadas con el registro en nuestro controlador . ¿Hay alguna manera mejor de no modificar más nuestro led_drv.ccontrolador?

Si led_drv.cno necesita modificarlo, este led_drv.ccontrolador es un controlador universal. Específico se puede ver 韦东山老师的《嵌入式Linux应用开发完全手册第2版》第五篇第3~7节进行学习.

Vamos a resolver brevemente lo siguiente:

====== 013

Debido a problemas de espacio, las partes específicas no se publicarán.

En las notas anteriores: Lenguaje C, conocimiento clave incrustado: también mencioné el significado de 通用y 专用en la función de devolución de llamada , puede comprender y profundizar su comprensión de estas dos palabras.

Aquí hemos aprendido un pensamiento muy importante y las habilidades de las capas de software, pero es solo un clic. El futuro todavía es un largo camino, y debemos seguir aprendiendo y seguir mejorando.

Lo anterior es este intercambio, si hay un error, por favor, ¡señale! Gracias

Materiales de referencia / aprendizaje:

  • Baiwen.com "Manual completo de desarrollo de aplicaciones Linux incrustadas 2ª edición"
  • Puntuacion Atom "I.MX6U Embedded Linux Driver Development Guide V1.2"
  • Wildfire "guía práctica de desarrollo i.MX Linux"
107 artículos originales publicados · elogiados 176 · 100,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/zhengnianli/article/details/105427304
Recomendado
Clasificación