Baking Pi – OK01 // Hágase la luz

Introducción

Esta es la primera parte del curso, en ella veremos una pequeña introducción a los conceptos básicos y aplicaremos estos conceptos para hacer encender el LED ACT (OK en algunos modelos) en nuestra Raspberry Pi.

Ensamblador

Lo primero de todo es saber en que lenguaje vamos a desarrollar nuestro programa. En este curso vamos a programar prácticamente todo en ensamblador.

Existen dos tipos de lenguaje, los de alto nivel y los de bajo nivel. Los de alto nivel no interactúan con el hardware directamente, sino que son interpretados y es el sistema operativo quien le dice a cada parte del ordenador (pantalla, altavoces…) lo que ha de hacer, un ejemplo de lenguaje de alto nivel sería HTML.

Por el contrario, los lenguajes de bajo nivel, le dan órdenes directamente al hardware, le dicen al procesador que es lo que tiene que hacer, a la pantalla que píxeles son los que ha de activar…

 

Todos los programas pasan por ser convertidos a ensamblador antes de ejecutarse. De forma que al escribir un programa directamente en ensamblador es mucho más rápido.

 

 

Al igual que ocurre con los lenguajes de alto nivel, existen muchos tipos de ensamblador, tantos como diferentes microprocesadores, cada uno de ellos diseñado para entender una clase de ensamblador. Es algo lioso, pero en nuestro caso como solo vamos a desarrollar nuestro sistema operativo para la Raspberry Pi no tendremos que preocuparnos de eso.

En nuestro caso, el procesador de la Raspberry Pi (menos la 3) utiliza la arquitectura ARMv6 por lo que estaremos programando para esa arquitectura.

 

Esto no debe asustaros, al igual que pasa con la mayoría de lenguajes de programación de alto nivel, visto uno, vistos todos.

Preparando el escenario

Antes de empezar a programar necesitamos algunas cosas, lo primero un editor de texto, yo os recomiendo Brackets, a mí personalmente me gusta bastante.

 

Lo segundo que vais a necesitar va a ser un compilador, sirve para transformar todo nuestro código en un archivo (llamado imagen) que nuestra Raspberry Pi es capaz de ejecutar, esta instalación es algo más complicada y suele dar algunos problemas.

 

Windows

 

Para Windows podéis instalar YAGARTO y los paquetes de MinGW.

Para instalarlo hay que acceder a la web de YAGARTO, descargar e instalar YAGARTO Tools y YAGARTO GNU ARM toolchain para Windows.

Puedes descargar MinGW aquí. Al terminar la instalación deberás reiniciar el ordenador.

Nota: YAGARTO tiene que ser instalado en  ‘C:\YAGARTO\’ no en ‘C:\Program Files\YAGARTO\’.

 

Mac

 

En Mac lo primero que tendréis que hacer es instalar Homebrew, para ello abrís “Terminal” y copiáis la siguiente linea:

 

/usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”

 

Después presionáis la tecla “Enter” y esperáis a que termine.

Seguramente os pida que instaléis alguna utilidad de Xcode, en ese caso simplemente hacéis click en “Instalar”.

Una vez haya terminado copiáis esta linea en Terminal:

 

brew cask install gcc-arm-embedded

 

Presionáis la tecla “Enter” y esperáis a que termine.

Y ya estaría!

 

Linux

 

En linux debéis copiar la siguiente linea en la Terminal:

 

sudo apt-get install gcc-arm-none-eabi

 

Después presionáis la tecla “Enter” y esperáis a que termine.

Todo listo!

 

Lo siguiente que debéis hacer es preparar una carpeta con todo lo necesario, para ello lo único que tenéis que hacer es descargar este archivo y descomprimirlo en el escritorio.

 

Ahora tendremos una carpeta llamada Baking Pi, con todo lo necesario, ahora mismo lo único que hay son un par de carpetas y los archivos necesarios para que el compilador cree la imagen con nuestro programa, que es la que copiaremos a la Raspberry.

 

El comienzo

Dentro de la carpeta “source” encontraremos un archivo llamado “main.s“. En ese archivo es en el que debemos escribir nuestro programa, para ello lo abriremos con el editor de texto Brackets (o alguno de vuestra preferencia).

 

La extensión “.s” se usa generalmente para todos los tipos de ensamblador, en nuestro caso ARMv6.

Los primeros comandos que debemos copiar son estos:

 

.section .init
.globl _start
_start:

 

Esto no hará nada en nuestra Raspberry, son simplemente instrucciones para el compilador. Éste se encargará de traducir nuestro código escrito en ensamblador a binario, para que la Raspberry lo entienda.

La primera linea le dice al compilador dónde debe poner nuestro código, cuando vayamos a compilarlo para generar la imagen que debemos copiar a la Raspberry, el compilador leerá el archivo kernel.ld y pondrá la sección init al comienzo. Especificar que debe ejecutarse primero es importante, si tuviésemos otros archivos aparte de “main.s” (algo que veremos más adelante) estos se ejecutarían alfabéticamente.

Las dos lineas siguientes son únicamente para evitar un error de compilación, con la linea anterior le hemos especificado que debe empezar en ese archivo, pero ahora debemos decirle por qué parte, y eso es lo que hacemos definiendo _start como un identificador global (accesible por cualquier otro archivo).

 

La primera línea

Ahora vamos a programar nuestra primera linea. En ensamblador, el ordenador simplemente va leyendo las lineas una por una, en orden, a no ser que especifiquemos lo contrario. Cada instrucción comienza en una nueva linea.

 

Vamos a copiar la siguiente linea:

 

ldr r0,=0x20200000

 

ldr reg,=val pone el número val en el registro reg

Lo que hace este comando es almacenar el número 0x20200000 en el registro r0, más adelante veremos que función tiene cada registro, ahora mismo lo único que debemos saber es que existen 13 registros de “Propósito general”, numerados del 0 al 12, y sirven para almacenar datos, pueden almacenar cualquier número entero entre 0 y 4.294.967.295. En este caso hemos usado el registro 0 (r0).

Aunque no lo parezca 0x20200000 es un número, lo que pasa es que está escrito en hexadecimal.

Existen varios sistemas de notación, algunos de ellos son: el binario, decimal, octal y hexadecimal.

 

Puedes aprender más sobre la notación hexadecimal aquí.

 

Pues bien, lo que hemos hecho ha sido almacenar el número 0x20200000 en el registro r0, esto puede parecer que no es mucho, pero lo es. En los ordenadores hay muchos trozos de memoria y dispositivos conectados (como por ejemplo el LED que queremos encender), cada uno de ellos tiene asignada una dirección. Al igual que cuando accedemos a una página web o mandamos una carta, tenemos que especificar una dirección que identifique el dispositivo que queremos.

 

En los ordenadores estas direcciones son números, y el número 0x20200000 es la dirección del controlador GPIO. Las direcciones las establecen los fabricantes, podrían haber usado cualquier otra (siempre que no se use para otra cosa).

Esta dirección la obtenemos mirando el manual del fabricante.

 

El controlador GPIO es el que se encarga de controlar los pines GPIO (General Purpose Input/Output, Entrada/Salida de Propósito General). El LED que queremos encender se encuentra conectado a un pin GPIO

Habilitando el pin

Para encender el LED debemos enviarle dos mensajes al controlador GPIO.

Nuestro LED se encuentra en el pin 16 (esto varía dependiendo del modelo), de modo que lo primero que debemos hacer es activar ese pin. Aquí se complica un poco la cosa, hay 54 pines en total, que se dividen en 6 sets (0-9, 10-19,  20-29, 30-39, 40-49, 50-54). Cada uno de estos sets controla 10 pines, menos el último, que controla 5.

Cada set ocupa 4 bytes, es decir, que si la dirección del controlador GPIO es 0x20200000, controlaremos los 10 primeros pines, al aumentar 4 bytes (0x20200000 + 4) estaremos controlando los 10 siguientes, y así sucesivamente. En nuestro caso el pin que debemos activar es el 16, por lo que deberemos ir al segundo set (0x20200000 + 4).

Una vez hayamos especificado a que grupo de pines nos estamos refiriendo, tenemos que especificar el pin concreto que queremos activar. Cada 3 bits hacemos referencia a un pin, es decir, el primer pin podríamos decir que se encuentra en el bit 0, el segundo en el bit 3, el tercero en el bit 6… Y así.

Dado que nuestro pin es el 16, y ya estamos en el set adecuado, debemos decirle que nuestro pin es el sexto de ese set, y como hay un pin cada 3 bits, es tan sencillo como multiplicar 3 x 6.

 

Ahora que sabemos la teoría, vamos a ver como podemos implementarla, para ello, copiamos estas lineas a continuación en nuestro editor de texto:

 

mov r1,#1
lsl r1,#18
str r1,[r0,#4]

 

La primera linea pone el número 1 (en notación decimal)  en el registro r1

 

mov reg,#val pone el número val en el registro reg

Podríamos haber usado ldr igual que antes, pero el comando mov es más rápido porque no tiene que acceder a la memoria. La pega es que solo sirve para almacenar números cuya representación binaria son ocho unos o ceros seguidos únicamente de ceros.

Esto no debe preocuparnos mucho, pero como solo tenemos que almacenar un uno, usamos mov, que es más rápido.

Con el número 1 almacenado en r1 pasamos a la siguiente linea.

 

lsl reg,val desplaza el número en reg escrito en binario val espacios a la izquierda

Con esta instrucción lo que hacemos es desplazar el número 1 almacenado en r1, en binario, 18 espacios a la izquierda, quedando así: 1000000000000000000. Es decir un 1 seguido de 18 ceros. Ahora veremos para que nos sirve esto.

 

str reg,[dest,#val] almacena el número en reg en la dirección dada por dest + val

Con la última linea lo que hacemos es cargar el número 1000000000000000000 a las dirección 0x20200000 + 4. La dirección es, la dirección del controlador GPIO + los 4 bytes que especifican que nuestro pin se encuentra entre el 10 y el 19.

En binario, cada 0 o 1 ocupa un bit, de modo que el número que hemos creado tendría 19 bits, un 1 seguido de 18 ceros. Pero en la Raspberry Pi, al cargar un número en una dirección, cargará un 1 en la parte baja (low en inglés), los ceros. Hay 18 ceros, que equivalen a 18 bits, por lo tanto cargará un 1 en el bit 18.

Ahora que ya tenemos listo el pin 16 podemos enviarle el mensaje para que encienda el LED.

Un haz de luz

El segundo mensaje que debemos enviar hará que encienda el LED

Para eso copiamos las siguientes lineas:

 

mov r1,#1
lsl r1,#16
str r1,[r0,#40]

 

La primera linea pone el número 1 en el registro r1. La segunda lo desplaza 16 espacios a la izquierda (en binario). Puesto que queremos encender el LED que está en el pin 16, tenemos que mandar un 1 en el bit 16, igual que hemos hecho antes. Aunque esto puede variar, porque al igual que antes, cuando los pines están en modo salida, es decir, activados y listos para encenderlos o apagarlos, se dividen en sets, pero esta vez en dos, de 4 bytes. 4 bytes son 32 bits, y le corresponde 1 bit a cada pin, por lo que el primer set va desde el pin 0 hasta el 31, y el segundo desde el 32 hasta el 54.

 

En este caso como nuestro pin se encuentra dentro del primer set no tendremos que hacer nada. En los nuevos modelos será necesario hacer algunos cambios pero eso lo veremos más adelante.

 

La última linea es la que se encarga de enviar el 1 en el bit 16 a la dirección dada por r0 + 40. El 40 viene dado por el fabricante, para encender el LED tenemos que apagar el pin, sí apagarlo. El número 28 encendería el pin, es decir, que apagaría el LED.

 

Actualización (nuevos modelos)

Este tutorial se quedó obsoleto hace tiempo, porque no funciona con los nuevos modelos. Eso se debe a que el LED se encuentra en un pin diferente y la dirección del controlador GPIO es otra.

 

Raspberry Pi 1 A y B – Dirección –> 0x20000000, número de pin del LED –> 16

 

Estos son los modelos para los que está hecho el tutorial, por lo que no hay que realizar ningún cambio, simplemente seguir el tutorial.

 

Raspberry Pi 1 A+, B+ y Zero – Dirección –> 0x20000000, número de pin del LED –> 47

 

En estos modelos la dirección no cambia, por lo que lo único que nos afecta es el cambio de pin.

La primera linea se queda igual:

ldr r0,=0x20200000

Ahora tenemos que enviarle el primer mensaje al controlador GPIO, para activar la salida del pin que queremos. En este caso las cosas cambian, puesto que se trata del pin 47. Lo primero de todo es especificar el set en el que se encuentra, como cada set controla 10 pines, debemos irnos al set número 4 (que controla los pines 40-49).

Como cada set ocupa 4 bytes, deberemos multiplicar 4 x 4.

Ahora debemos especificar cual es el pin que queremos dentro de nuestro set, como cada 3 bits tenemos un pin, y el nuestro es el séptimo, es algo tan simple como 3 x 7.

De modo que el código de la sección “Habilitando el pin” adaptado para este modelo quedaría así:

mov r1,#1
lsl r1,#21
str r1,[r0,#16]

Por último, para encender el led, debemos hacer algunos cambios, en primer lugar, como ya vimos, cuando están en modo salida se dividen en dos sets de 4 bytes, el primer set controla los primeros 32 pines, y el segundo los 22 restantes. Como nuestro pin se encuentra en el segundo set, tenemos que sumar 4 a la dirección del controlador GPIO

Al haber especificado que nuestro pin está en el segundo set, no tendremos que mandar un 1 al bit 47 puesto que nuestro pin en el segundo set el 15 (47 – 32).

Además, aquí para encender el LED han decidido que hay que encender el pin (al contrario que para el modelo original). Por lo que en vez de 40 usamos 28.

Esos 28 se suman a la dirección del controlador, pero como además tenemos que sumarle los 4 bytes que indican que el pin se encuentra en el segundo set, tendríamos que sumarle 32 a la dirección del controlador GPIO.

Quedando el código de la sección “Un haz de luz” así:

mov r1,#1
lsl r1,#15
str r1,[r0,#32]

 

Raspberry Pi 2 B – Dirección –> 0x3F000000, número de pin del LED –> 47

 

En este modelo la dirección del controlador GPIO es 0x3F000000 por lo que la primera linea debemos cambiarla por:

ldr r0,=0x3F000000

Ahora tenemos que enviarle el primer mensaje al controlador GPIO, para activar la salida del pin que queremos. En este caso las cosas cambian, puesto que se trata del pin 47. Lo primero de todo es especificar el set en el que se encuentra, como cada set controla 10 pines, debemos irnos al set número 4 (que controla los pines 40-49).

Como cada set ocupa 4 bytes, deberemos multiplicar 4 x 4.

Ahora debemos especificar cual es el pin que queremos dentro de nuestro set, como cada 3 bits tenemos un pin, y el nuestro es el séptimo, es algo tan simple como 3 x 7.

De modo que el código de la sección “Habilitando el pin” adaptado para este modelo quedaría así:

mov r1,#1
lsl r1,#21
str r1,[r0,#16]

Por último, para encender el led, debemos hacer algunos cambios, en primer lugar, como ya vimos, cuando están en modo salida se dividen en dos sets de 4 bytes, el primer set controla los primeros 32 pines, y el segundo los 22 restantes. Como nuestro pin se encuentra en el segundo set, tenemos que sumar 4 a la dirección del controlador GPIO

Al haber especificado que nuestro pin está en el segundo set, no tendremos que mandar un 1 al bit 47 puesto que nuestro pin en el segundo set el 15 (47 – 32).

Además, aquí para encender el LED han decidido que hay que encender el pin (al contrario que para el modelo original). Por lo que en vez de 40 usamos 28.

Esos 28 se suman a la dirección del controlador, pero como además tenemos que sumarle los 4 bytes que indican que el pin se encuentra en el segundo set, tendríamos que sumarle 32 a la dirección del controlador GPIO.

Quedando el código de la sección “Un haz de luz” así:

mov r1,#1
lsl r1,#15
str r1,[r0,#32]

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *