Electrónica y programación para Microcontroladores.

Libros técnicos para electrónica programable.

Email
Contactanos en:

consultas@firtec.com.ar

Arduino

Una de las grandes diferencias del microcontrolador RP2040 usado en Raspberry PI PICO con cualquier otro controlador, son los bloques PIO, esta serie de microcontroladores esclavos de los dos ARM Cortex M0 integrados que forman su CPU de doble núcleo. Son ocho máquinas de estado que están dedicadas al control de los pines GPIO con independencia total de los núcleos Cortex y que pueden ejecutar sus propios programas para entregar o recibir datos al la CPU.

Estas máquinas de estado están separadas en dos bloques de cuatro máquinas por bloque. Estas máquinas de estados son como procesadores elementales que pueden manejar datos en los pines pero carecen de Unidad de Aritmética y Lógica (ULA) por lo que no pueden realizar operaciones matemáticas ni lógicas solo cuentas incrementales que se utilizan en los bucles.
Si bien el RP2040 puede funcionar a una frecuencia de reloj de 133 Mhz en Raspberry Pico su frecuencia de trabajo máxima es 125 Mhz.
Cada una de estas máquinas puede funcionar a una frecuencia de reloj que se pude configurar con un pre-divisor del reloj principal, el ajuste de frecuencia admite un rango que va desde 1998 Hz (Se toma como frecuencia mas baja 2Khz) a 125.000.000 Hz.
Este rango de frecuencia se obtiene dividiendo el reloj principal (125 Mhz) por un entero de 16 bits.
Cada bloque PIO se identifican como pio 0 y pio 1 y se programan con un tipo particular de ensamblador que solo tiene nueve comandos por lo que a pesar de se ensamblador no resulta tan complejo.
Los PIO son muy útiles cuando necesitamos desarrollar protocolos de comunicación especiales, en la mayoría de los microcontroladores, para implementar un protocolo que no esté soportado por hardware se necesita programar el protocolo y procesar la información bit por bit (lo que se conoce como bit-banging). Esta técnica presenta varios problemas por ejemplo la sincronización de los datos y el uso del procesador principal.
Para cada máquina existe un registro FIFO de entrada y uno de salida, estos FIFO pueden manejar solo cuatro palabras de 32 bits. También hay en cada máquina dos registros de propósitos generales llamados X, Y de 32 bits cada uno.

Como se dijo, cada PIO tiene nueve instrucciones todas se ejecutan en un ciclo de CPU, que les permite realizar diversas tareas de forma simultánea al procesador principal.

  • IN Desplaza bits al registro de entrada
  • OUT Desplaza bits desde el registro de salida a donde se necesite
  • PUSH Envía datos al FIFO RX
  • PULL Envía datos al FIFO TX
  • MOV Mueve datos desde un origen a un destino especificado
  • IRQ Activa o desactiva la bandera de interrupción
  • SET Escribe datos a un destino
  • WAIT Pausa hasta que una acción en particular ocurra
  • JMP Se mueve a un punto diferente del código

La memoria de instrucciones del PIO pude contener hasta 32 comandos y cada máquina tiene un banco con ocho banderas de interrupción que se pueden usar para sincronizar las máquinas entre si o informar a la CPU que hay datos para tratar.
Cada máquina tiene su propio contador de programa (PC) por lo tanto cada una puede ejecutar su propio programa independiente de otra máquina.
Sin embargo, las cuatro máquinas de estado deben usar la memoria de instrucciones compartidas para su programa PIO que puede almacenar hasta 32 instrucciones.
Entonces
se podrían escribir cuatro programas separados para esas cuatro máquinas de estado, siempre que el número total de instrucciones no exceda los 32 posibles. Esos programas se ejecutarán independientemente entre sí, solo sincronizados por el reloj del sistema principal que se pueden dividir individualmente para cada máquina de estado.
Un ejemplo de uso podría ser una rutina que recibe datos seriales por dos pines y transfiera a la CPU los caracteres recibidos.
La siguiente función hace eso sin intervención de ninguna biblioteca y evita tener que estar consultando constantemente el registro de recepción de la UART.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def uart_rx():
    label("inicio") # Rótulo para marcar el inicio
    wait(0, pin, 0) # Espera por el bit de inicio (pin a nivel bajo el tiempo de un bit)
    # Carga el registro x con la cantidad de bits desde el primer bit de datos y espera
    # 10 ciclos + wait + set 12 ciclos. (12*13uS = 156uS)
    set(x, 7)                 [10]
    label("bit_bucle")	# Rótulo para el inicio del bucle contador de bits
    in_(pins, 1)  	# Desplaza un bit desde el pin 3 RX al registro de desplazamiento
    			# Cada bucle toma 8 ciclos, 6 + dec + jmp = 8 (8*13uS = 104uS)
    jmp(x_dec, "bit_bucle")     [6]
    jmp(pin, "terminado")  # Si el pin esta a nivel alto llegó el bit de STOP todo ha salido bien!!
    			# OPSS!! Si el programa pasa por este punto algo salió mal
    irq(block, 4)      	# Coloca bandera para avisar que los datos del FIFO no son válidos
    wait(1, pin, 0) 	# Espera que el pin de datos este bajo
    jmp("inicio") 	# No hacer nada y retorna al inicio
    label("terminado") 	# Rótulo final, todo ha salido bien y se ha recibido un carácter!!
    push(block)		# Colocar el contenido del registro FIFO en el registro de salida para procesarlo

El ejemplo supone que se ha fijado el reloj de la máquina de estado a una frecuencia de 76800 Hz con tiempo de CPU de 13uS. Las comunicaciones se han fijado a 9600 baudios con un tiempo de bits de 104.1 uS.

(Material extraído de uno de nuestros libros para MicroPython).