PIC16F84 – Control PID

PIC16F84 – Control PID

Problema propuesto

Emular un Controlador Discreto PID con el PIC16F84.

Utilizar una gestión de tiempos con interrupción por temporizador. Se supone que la referencia es un valor interno, Referencia. Para simplificar se supone que el actuador y el sensor manejan la información en complemento a 2.

  • Cada 0.01 debe leer el sensor, LeerPortB.
  • Cada 0.1s debe:
    • Calcular MedidaFiltrada como un promedio de las 8 últimas lecturas del sensor. Se puede realizar la media sumando dos elementos y un desplazamiento a la derecha y así sucesivamente hasta cerrar el árbol binario de medias, por eso he elegido 8 elementos.
    • Calcular el Control.
      • ErrorAnterior=Error
      • Error= Referencia – MedidaFiltrada
      • Integral= Integral + Error
      • Derivativa= Error- ErrorAnterior
      • Control= Kp*Error+Ki*Integral+Kd*Derivativa
    • Guardar telemetrías en la EEPROM.
  • Simular el sistema (en lazo abierto) inyectando estímulos en PortB que contengan ruido.
  • El PIC16F84A no es el adecuado para implementar un PID, pues además de su ALU tan limitada, no dispone ni de conversores analógico-digital ADC para manejar sensores analógicos, ni de generadores de modulación de ancho de pulsos (PWM) para manejar actuadores. Un microntrolador muy básico que ya dispone de estos recursos es el PIC16F87.

Solución

Este programa ya supone 326 líneas de código, y en él se puede decir que se exprime la capacidad del microcontrolador al máximo. En resumen, programaremos para que haya una interrupción cada centésima de segundo (0,01s) en la que leeremos el sensor a través del puerto B e iremos guardando las lecturas realizadas a través de la subrutina LECTURAS. Debido a que solamente disponemos de un temporizador, utilizaremos un contador decreciente para saber cuándo han transcurrido 0,1 segundos (10 centésimas o 10 desbordamientos) y aprovecharemos para llamar a tres subrutinas. Primeramente filtraremos la medida realizando una media de las ocho últimas lecturas recogidas mediante la subrutina CALCFIL Como no disponemos de instrucción para dividir valores debemos improvisar con su equivalente en ensamblador, rotar a la derecha. Para ello sumaremos por parejas y rotaremos a la derecha guardando el resultado en la variable par mayor.

Árbol binario del cálculo de la medida filtrada
Árbol binario del cálculo de la medida filtrada

A continuación realizaremos el cálculo del control PID mediante la subrutina CALCONTROL En realidad no tratamos nada nuevo, pero supone unas cuantas líneas de código simplemente para realizar tres productos y tres sumas. Ya que es una simple simulación con limitaciones y en lazo abierto hay que tener en cuenta las siguientes consideraciones:

  • Las constantes Ki, Kp, Kd se dejan con un valor de 1.
  • No se corrige el Windup.
  • No se tienen en cuenta ni el overshoot ni el undershoot.
  • No se tiene en cuenta el error negativo.

Finalmente guardamos las telemetrías en la eeprom mediante la subrutina GUARDAR. Aunque es algo que tratamos por primera vez, no supone mayor complicación ya que el propio fabricante indica la manera correcta de leer y escribir de/en la eeprom.

Diagrama de flujo

Diagrama de flujo
Diagrama de flujo

Código

;------------------------------------------------------------------
		__CONFIG   _CP_OFF &  _WDT_OFF & _PWRTE_ON & _XT_OSC
		list 	p=16f84a
		include "p16f84a.inc"
;------------------------------------------------------------------
;CBLOCK   			0x0C
;Telemetria
;ENDC
ORG			0x2100		; Dirección 0 de la EEPROM
DE			0x00		; Telemetría a cero.
Dato1		equ	0x0C
Dato2		equ	0x0D
DH		equ 	0x0E 		; byte alto
DL		equ 	0x0F 		; byte bajo
NumA		equ	0x10
NumB		equ	0x11
AxB		equ 	0x12
Conta		equ	0x13		; Contador
RegW		equ	0x14		; Registro W
RegS		equ	0x15		; Registro Status
FlagL		equ	0x16		; Flag Lecturas
n1		equ	0x17		; 1ªLectura
n2		equ	0x18		; 2ªLectura
n3		equ	0x19		; 3ªLectura
n4		equ	0x20		; 4ªLectura
n5		equ	0x21		; 5ªLectura
n6		equ	0x22		; 6ªLectura
n7		equ	0x23		; 7ªLectura
n8		equ	0x24		; 8ªLectura
Error		equ	0x25		; Error
ErrorAnt	equ	0x26		; Error Anterior
Integ		equ	0x27		; Integral
Deriv		equ	0x28		; Derivada
Control		equ	0x29		; Control
Referen		equ	0x30		; Referencia
MedFil		equ	0x30		; Medida filtrada
kp		equ	0x31		; Kp
ki		equ	0x32		; Ki
kd		equ	0x33		; Kd
;------------------------------------------------------------------
			org 	0x00
			goto 	INICIO
			org 	0x04
			goto	INT_Timer
INICIO 
			bsf	status,5	; Banco 1 (RP0)
			movwf	b'00000000'
			movwf	TRISB		; Puerto B como salida
			movwf	b'00011111'
			movwf	TRISA		; Puerto A como entrada
			clrw			; Borro W
			movlw 	0x07 		; Cargo W con 00000111 (PSAx,1,1,1)
			movwf 	option_reg 	; Divisor = 256
			bcf 	status,5	; Banco 0 (RP0)
			movlw 	0x88 		; Cargo W con 10101000
			movwf 	intcon 		; Habilitamos GIE y RBIE
			clrf 	portb 		; Borro PORTB
			clrf 	porta 		; Borro PORTA
			clrf	Conta		; Borro Conta
			clrf	Dato1		; Borramos
			clrf	Dato2		; Borramos
			clrf	NumA		; Borramos
			clrf	NumB		; Borramos
			clrf	AxB		; Borramos
			movlw	0xA		; 10cs = 0,1s
			movwf	Conta	
			movlw 	0xD9 		; Cargo W con 0x00-0x27
			movwf 	tmr0 		; Lo paso a TMR0
			movlw 	0x1 		; Cargo W con 1
			movwf 	FlagL 		; Inicializo FlagL
PRINCIPAL
			; Pasamos el cálculo al actuador (PORTA)
			movf	Control,W	; W = Control
			movwf	PORTA		; Lo pasamos a PORTA
			goto	PRINCIPAL
;GESTIONO INTERRUPCIÓN-------------------------------------------------
;0,01S = 10MS = 1CS --> LEER PORTB ------------------------------------
;0,1S = 100MS = 10CS -> CALC MED.F -> CALC CONTROL -> GUARDAR DATO ----
;----------------------------------------------------------------------
INT_Timer
			bcf 	INTCON,GIE 	; Deshabilitar interrupciones
			movwf 	RegW		; Guardamos W
			swapf	STATUS,W	; Invertimos nibbles
			movwf	RegS		; Guardamos estado
			call	LECTURAS 	; Leo puerto B
			decfsz	Conta,F		; Conta -=1
			goto	SIGO		; No hemos acabado
			movlw	0xA		; 10cs = 0,1s
			movwf	Conta		; Conta = 0xA
			call	CALCFIL		; Calculo Medida filtrada
			call	CALCONTROL	; Calculo Control
			movf	Control,W	; W = Control
			call	GUARDAR		; Guardo telemetrías
SIGO
			movlw 	0xD9 		; Cargo W con 0x00-0x27
			movwf 	tmr0 		; Lo paso a TMR0
			bcf 	INTCON,T0IF ; Limpiar bandera de interrupción
			swapf	RegS,W		; Invertimos nibbles de RegS
			movwf	STATUS		; Restauramos estado
			swapf	RegW,fin	; Invertimos nibles
			swapf	RegW,W		; Restauramos W
			bsf 	INTCON,GIE 	; Habilitar interrupciones
			retfie			; Vuelvo de la interrupción
;-------------------------------------------------------------------------
;SUBRUTINA COGER LECTURAS-------------------------------------------------
;-------------------------------------------------------------------------
LECTURAS
			movf	1,W		;
			subwf	FlagL,W		;
			btfsc	status,Z	;
			goto	FLAGUNO		; 1ªLectura
			movf	2,W		;
			subwf	FlagL,W		;
			btfsc	status,Z	;
			goto	FLAGDOS		; 2ªLectura
			movf	3,W		;
			subwf	FlagL,W		;
			btfsc	status,Z	;
			goto	FLAGTRES	; 3ªLectura
			movf	4,W		;
			subwf	FlagL,W		;
			btfsc	status,Z	;
			goto	FLAGCUATRO	; 4ªLectura
			movf	5,W		;
			subwf	FlagL,W		;
			btfsc	status,Z	;
			goto	FLAGCINCO	; 5ªLectura
			movf	6,W		;
			subwf	FlagL,W		;
			btfsc	status,Z	;
			goto	FLAGSEIS	; 6ªLectura
			movf	7,W		;
			subwf	FlagL,W		;
			btfsc	status,Z	;
			goto	FLAGSIETE	; 7ªLectura
			goto	FLAGOCHO	; 8ªLectura
FLAGUNO
			movf	portB,W		;
			sublw 	0x00000000 	; Complemento a 2
			movwf	n1		; 1ª lectuta
			incf	FlagL,1		; FlagL +=1
			goto	FIN		;
FLAGDOS
			movf	portB,W		;
			sublw 	0x00000000 	; Complemento a 2
			movwf	n2		; 2ª lectuta
			incf	FlagL,1		; FlagL +=1
			goto	FIN		;
FLAGTRES
			movf	portB,W		;
			sublw 	0x00000000 	; Complemento a 2
			movwf	n3		; 3ª lectuta
			incf	FlagL,1		; FlagL +=1
			goto	FIN		;
FLAGCUATRO
			movf	portB,W		;
			sublw 	0x00000000 	; Complemento a 2
			movwf	n4		; 4ª lectuta
			incf	FlagL,1		; FlagL +=1
			goto	FIN		;			
FLAGCINCO
			movf	portB,W		;
			sublw 	0x00000000 	; Complemento a 2
			movwf	n5		; 5ª lectuta
			incf	FlagL,1		; FlagL +=1
			goto	FIN		;
FLAGSEIS
			movf	portB,W		;
			sublw 	0x00000000 	; Complemento a 2
			movwf	n6		; 6ª lectuta
			incf	FlagL,1		; FlagL +=1
			goto	FIN		;
FLAGSIETE
			movf	portB,W		;
			sublw 	0x00000000 	; Complemento a 2
			movwf	n7		; 7ª lectuta
			incf	FlagL,1		; FlagL +=1
			goto	FIN		;
FLAGOCHO
			movf	portB,W		;
			sublw 	0x00000000 	; Complemento a 2
			movwf	n8		; 8ª lectuta
			movf	1,W		; Reinicio flag
			movwf	FlagL		;
FIN
			return			; fin subrutina
;-------------------------------------------------------------------------
;SUBRUTINA FILTRAR MEDIDA-------------------------------------------------
;-------------------------------------------------------------------------
CALCFIL
			;(n1+n2)/2
			movf	n1,W		; W = n1
			addwf	n2,1		; n1+n2 y guardo en n2
			rrf	n2,1	; roto a dcha y guardo en n2
			;(n3+n4)/2
			movf	n3,W		; W = n3
			addwf	n4,1		; n3+n4 y guardo en n4
			rrf	n4,1		; roto a dcha y guardo en n4
			;(n5+n6)/2
			movf	n5,W		; W = n5
			addwf	n6,1		; n5+n6 y guardo en n6
			rrf	n6,1		; roto a dcha y guardo en n6
			;(n7+n8)/2
			movf	n7,W		; W = n7
			addwf	n8,1		; n7+n8 y guardo en n8
			rrf	n8,1		; roto a dcha y guardo en n8
			;(n2+n4)/2
			movf	n2,W		; W = n2
			addwf	n4,1		; n2+n4 y guardo en n4
			rrf	n4,1		; roto a dcha y guardo en n4
			;(n6+n8)/2
			movf	n6,W		; W = n6
			addwf	n8,1		; n6+n8 y guardo en n8
			rrf	n8,1		; roto a dcha y guardo en n8
			;(n4+n8)/2
			movf	n4,W		; W = n4
			addwf	n8,1		; n4+n8 y guardo en n8
			rrf	n8,0		; roto a dcha y guardo en W
			movwf	MedFil		; Guardo la Medida Filtrada
			return			; fin subrutina
;-------------------------------------------------------------------------
;SUBRUTINA CALCULAR CONTROL-----------------------------------------------
	;ErrorAnterior=Error
	;Error= Referencia - MedidaFiltrada
	;Integral= Integral + Error
	;Derivativa= Error- ErrorAnterior
	;Control= Kp*Error+Ki*Integral+Kd*Derivativa
;-------------------------------------------------------------------------
CALCONTROL
			; Valores regulación PID
			movlw	0x1		; w=1
			movwf	kp		; Kp=1
			movlw	0x1		; w=1
			movwf	ki		; Ki=1
			movlw	0x1		; w=1
			movwf	kd		; Kd=1
			; Error = Referencia - MedidaFiltrada
			movf	Error,W		; W = Error
			movwf	ErrorAnt	; ErrorAnt = Error
			movf	MedFil,W	; W = MedFil
			subwf	Referen,0 	; f - w → Referen - MedFil
			movwf	Error		; Error = Referen - MedFil
			; Integral = Integral + Error
			addwf	Integ,1		; Integ += Error
			; Derivativa = Error - ErrorAnterior
			movf	ErrorAnt,W	; W = ErrorAnt
			subwf	Error,0 	; f - w → Error - ErrorAnt
			movwf	Deriv		; Deriv = Error - ErrorAnt
			; Control = Kp*Error+Ki*Integral+Kd*Derivativa
			movf	kp,W		; W=Kp
			movwf	Dato1		; Dato1 = Kp
			movf	Error,W		; W=Error
			movwf	Dato2		; Dato2 = Error
			call	PRODUCTO	; Multiplico
			addwf	Control,1	; Control = Kp*Error
			movf	ki,W		; W=Ki
			movwf	Dato1		; Dato1 = Ki
			movf	Integ,W		; W=Integ
			movwf	Dato2		; Dato2 = Integ
			call	PRODUCTO	; Multiplico
			addwf	Control,1	; Control = Kp*Error+Ki*Integral
			movf	kd,W		; W=Kd
			movwf	Dato1		; Dato1 = Kd
			movf	Deriv,W		; W=Deriv
			movwf	Dato2		; Dato2 = Deriv
			call	PRODUCTO	; Multiplico
			addwf	Control,1	; Control = Kp*Err+Ki*Inte+Kd*Deriv
			return			; fin subrutina
;-------------------------------------------------------------------------
;SUBRUTINA GUARDAR TELEMETRÍAS--------------------------------------------
;-------------------------------------------------------------------------
GUARDAR
			CBLOCK
			GuardaINTCON
			ENDC
			bcf	STATUS,5		; Banco 0
			movwf	EEDATA			; El byte a escribir
			movf	INTCON,W		; Valor anterior de INTCON
			movwf	GuardaINTCON
			bsf	STATUS,5		; Banco 1
			bcf	INTCON,GIE		; Deshabilita interrupciones
			bsf	EECON1,WREN		; Habilita escritura
			movlw	0x55
			movwf	EECON2
			movlw	0xAA
			movwf	EECON2
			bsf	EECON1,WR		; Inicia la escritura.
TERMINA
			btfsc	EECON1,WR		; ¿Fin de la escritura?
			goto	TERMINA			; No
			bcf	EECON1,WREN		; No escritura en EEPROM
			bcf	EECON1,EEIF		; Limpia flag
			bcf	STATUS,5		; Banco 0
			movf	GuardaINTCON,W 	; Restaura INTCON
			movwf	INTCON
			return				; fin subrutina
;-------------------------------------------------------------------------
;SUBRUTINA MULTIPLICACIÓN-------------------------------------------------
;-------------------------------------------------------------------------
PRODUCTO
			clrf 	DH
			clrf	DL
			movf 	Dato1,W		; W = multiplicador
			btfsc	status,Z	; Salta si Z=1
			return			; Z=0 hemos terminado
			movf 	Dato2,W		; W = multiplicador
			btfsc	status,Z	; Salta si Z=1
			return			; Z=0 hemos terminado
SIGUE
			movf	DL,W		; W=DL
			addwf	Dato1,W		; W += multiplicando
			movwf	DL		; DL=W
			btfsc	status,C	; Salta si C=0
			incf	DH,F		;
			decfsz	Dato2,F		; multiplicador-1
			goto	SIGUE		; no hemos acabado
			movwf	DL		; DL=W
			movwf	AxB		; Producto=W
			;movwf 	PORTB		; Puerto B=W
			clrf	Dato2
			return			; fin subrutina
;------------------------------------------------------------------
			end			; fin
;------------------------------------------------------------------

Deja un comentario

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

2 × cinco =