Autor Tema: SysTick en KL46z256VLL4  (Leído 4052 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado mojo

  • PIC10
  • *
  • Mensajes: 23
SysTick en KL46z256VLL4
« en: 07 de Diciembre de 2016, 23:37:27 »
Hola buenas!

Estoy haciendo un proyecto que nos han propuesto en la Universidad consistente en diseñar una máquina de estados finitos tipo Moore para resolver un problema (intersección con semáforos) y representarla en el código como una estructura de datos... en fin no tuve demasiado problema haciendo esto, si a alguien le interesa puedo subir el código o más información. El quebradero de cabeza viene al intentar utilizar SysTick para ir de un estado a otro, en lugar de de usar ifs, bucles, etc.

He mirado en que consiste SysTick y creo que comprendo el funcionamiento, pero no encuentro ningún ejemplo o explicación por el que pueda guiarme. He buscado por internet y en este foro y encuentro proyectos de gente bastante similares al mío, pero siempre utilizan otra placa que no es la mía o me da algún fallo (Undefined symbol casi siempre) al compilar y nunca logro arreglarlo al 100%.

No quiero que penseis que no he buscado bastante, en serio que me he roto la cabeza... :5] Pero soy nuevo en esto de los microcontroladores y me está resultando muy complicado empezar.

Si alguien puede ayudarme o enseñarme algún ejemplo en el que haya usado SysTick y que me pueda servir lo agradecería mucho.

P.D. Estoy utilizando Keil uVision4

Saludos y gracias  :-)

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:SysTick en KL46z256VLL4
« Respuesta #1 en: 08 de Diciembre de 2016, 02:59:17 »
Lamentablemente no se que librerias posea. Pero si usas KEIL debes tener CMSIS a mano, con lo cual podrias cambiar los registros uno por uno.

Son unos cuantos para configurar el Systick, no es demasiado, tenes un 1 registro que es de configuracion, otro registro donde cargas el valor limite. y listo.

SYST_CSR - SysTick Control and Status Register.
SYST_RVR - SysTick Reload Value Register
SYST_CVR - SysTick Current Value Register

El systick es un contador decreciente, por lo cual vos vas a cargar un numero, y este va a ir restando por cada pulso de reloj le llegue. Una ves que este en 0x00000 y se reste 1 mas, Ocurre que solo el hardware toma el valor del registro RVR ( Reload ) y lo pone en el Current.

Muchas opciones para controlar el systick no tenes.

En el registro SYST_CSR tenes:

Bit 2: CLKSOURCE , Si es 0 utiliza algun reloj externo, si es 1 utiliza el mismo reloj del procesador
Bit 1: TICKINT, habilitacion de la interrupcion, obviamente luego hay que limpiar algun pendiente y habilitarlo desde el NVIC.
Bit 0: ENABLE, si pones un 1 el Systick comienza a funcionar.


Entonces supongamos que tenemos un reloj de 10Mhz, esto es un periodo de 1/10Mhz = 0.1us , o mejor dicho cada 0.1us el systick va a restar 1
Suponete que quisiera que la interrupcion ocurra cada 100ms, por lo tanto deberia contar X ciclos de reloj, esto es : 100ms / 0.1us = 1.000.000

Entonces en el registro RVR debo cargar ese valor menos 1 ( ya que el 0 cuenta ), Por lo tanto me quedaria:

Código: C
  1. SYST_RVR = 999999; // Cargo valor
  2. SYST_CVR = 0; // Borro valor actual asi comienza de 0
  3. SYST_CSR = 0b111; // Selecciono el clock a usar, activo interrupciones, y activo el systick
  4.  
  5. //Aca limpiar algun pendiente del systick y activar el NVIC

El valor de recarga ( RVR ) puede ser de 1 (0x1) a 16.777.215 (0xFFFFFF), observa que no son los 32 bits completos.

PD: Tal ves usen otros nombres.

Y no se si se puede utilizar esto:
(Creo que es parte del CMSIS, no me fije, pero podrias probar, agregando el correspondiente include y las rutas de include)

Código: C
  1. SysTick_Config(1000);
  2.  
  3.         // Seteamos la prioridad a nuestra preferencia
  4.         NVIC_SetPriority(SysTick_IRQn,3);
  5.  
  6.         // Limpiamos el interrupt
  7.         NVIC_ClearPendingIRQ(SysTick_IRQn);
  8.  
  9.         // Habilitamos el IRQ
  10.         NVIC_EnableIRQ(SysTick_IRQn);

Algunos usan:

Código: C
  1. SysTick_Config(SystemCoreClock / 1000);     // 1ms

Donde SystemCoreClock suele ser una constante, y tiene el valor de la frecuencia del clok de entrada al procesador, como esa frecuencia te va a dar 1 segundo, si lo divido por 1000 seria 1 ms.. O para que quede mas claro:

Código: C
  1. SysTick_Config(SystemCoreClock * 1*10^-3);     // 1ms
  2.         SysTick_Config(SystemCoreClock * 1*10^-6);     // 1us
  3.         SysTick_Config(SystemCoreClock * 1*10^-4);     // 100us

Y si observas, 1*10^-3 segundos es 1ms, a la menos 6 es 1us.. y queda mas simple

Desconectado mojo

  • PIC10
  • *
  • Mensajes: 23
Re:SysTick en KL46z256VLL4
« Respuesta #2 en: 08 de Diciembre de 2016, 07:54:09 »
Muchas gracias, ¡ahora me pongo a ello!   :-)

Desconectado mojo

  • PIC10
  • *
  • Mensajes: 23
Re:SysTick en KL46z256VLL4
« Respuesta #3 en: 08 de Diciembre de 2016, 10:09:24 »
Hola, he conseguido algo finalmente gracias a lo que me pasó KILLERJC pero estoy teniendo algún problemilla:

Código: C
  1. int main (void) {
  2.        
  3.         SysTick->LOAD = 0xFFFFFF;
  4.         //Empiezo a contar en este valor
  5.  
  6.         SysTick->VAL = 0x0;
  7.         //Borro el valor actual que había anteriormente
  8.  
  9.         SysTick->CTRL = 0x111;
  10.         //Reloj del procesador, habilito interrupción, SysTick empieza a funcionar
  11.        
  12.         SysTick_Config(SystemCoreClock);
  13.         //Supuestamente hace que cada click dure 1 segundo
  14.        
  15.         NVIC_ClearPendingIRQ(SysTick_IRQn);
  16.         //Borro los pendientes
  17.  
  18.         NVIC_SetPriority(SysTick_IRQn, 3);
  19.         //Le asigno una prioridad a la interrupción
  20.  
  21.         NVIC_EnableIRQ(SysTick_IRQn);
  22.         //Habilito la interrupción
  23.        
  24.         while(1) {
  25.  
  26.                 if (SysTick->VAL < 0xFFFFF) {
  27.                        
  28.                         setLedR(1);
  29.                         //Enciendo LED rojo
  30.  
  31.                 } else {
  32.  
  33.                         setLedR(0);
  34.                         //Apago LED rojo
  35.  
  36.                 }
  37.  
  38.         }
  39.  
  40. }

Este código hace que el ciclo completo dure unos 5 segundos aproximadamente, encendiendo una vez el LED rojo en cada ciclo durante 0.5 segundos más o menos, el resto del tiempo estaría apagado.

Sirve para ver y comprender el funcionamiento de SysTick, pero el problema es que creo que SysTick_Config no está funcionando. De hecho da igual que lo ponga o que no lo ponga, el resultado es el mismo. He probado a ponerlo antes de darle valores a los registros, depués, etc y sigue fallando...

¿A que puede deberse esto?

Saludos ;-)

Desconectado mojo

  • PIC10
  • *
  • Mensajes: 23
Re:SysTick en KL46z256VLL4
« Respuesta #4 en: 08 de Diciembre de 2016, 10:14:07 »
Según como lo entiendo yo, el siguiente código:

Código: C
  1. int main (void) {
  2.            
  3.             SysTick->LOAD = 0x14;
  4.             //Empiezo a contar en este valor 20 en decimal
  5.      
  6.             SysTick->VAL = 0x0;
  7.             //Borro el valor actual que había anteriormente
  8.      
  9.             SysTick->CTRL = 0x111;
  10.             //Reloj del procesador, habilito interrupción, SysTick empieza a funcionar
  11.            
  12.             SysTick_Config(SystemCoreClock);
  13.             //Supuestamente hace que cada click dure 1 segundo
  14.            
  15.             NVIC_ClearPendingIRQ(SysTick_IRQn);
  16.             //Borro los pendientes
  17.      
  18.             NVIC_SetPriority(SysTick_IRQn, 3);
  19.             //Le asigno una prioridad a la interrupción
  20.      
  21.             NVIC_EnableIRQ(SysTick_IRQn);
  22.             //Habilito la interrupción
  23.            
  24.             while(1) {
  25.      
  26.                     if (SysTick->VAL < 0xA) {
  27.                            
  28.                             setLedR(1);
  29.                             //Enciendo LED rojo
  30.      
  31.                     } else {
  32.      
  33.                             setLedR(0);
  34.                             //Apago LED rojo
  35.      
  36.                     }
  37.      
  38.             }
  39.      
  40.     }

Debería encender el LED rojo durante 10 segundos, y apagarlo durante otros 10, teniendo un ciclo total de 20 segundos.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:SysTick en KL46z256VLL4
« Respuesta #5 en: 08 de Diciembre de 2016, 12:51:12 »
Ojo que estas activando la interrupcion, deberias tener una rutina de interrupcion... Si no vas a usar la interrupcion, entonces con esto te basta:

Código: C
  1. SysTick->LOAD = 0xFFFFF;
  2.             //Empiezo a contar en este valor 20 en decimal
  3.      
  4.             SysTick->VAL = 0x0;
  5.             //Borro el valor actual que había anteriormente
  6.      
  7.             SysTick->CTRL = 0x101;
  8.             //Reloj del procesador, habilito interrupción, SysTick empieza a funcionar
  9.  
  10.             while(1)
  11.            {
  12.      
  13.                         if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)
  14.                        {
  15.                             // Invierto el valor segun como estaba
  16.                             toggleLedR();
  17.                         }
  18.             }

No lo nombre antes porque pense que se iba a hacer con interrupciones, pero existe un bit mas en el registro de control del Systick, que es el COUNTFLAG, este bit se pone a 1 cuando el contador pasa del 0x0000 al valor del registro LOAD. Cuando lo lees solo se pone a 0, para poder detectar cuando recarga nuevamente. Entonces cuando se termine el tiempo ese bit va a estar en 1, es el bit la posicion 16 o (1 << 16), una ves leido con el if, se pone a 0 y tendrias que esperar hasta que se ponga a 1 de vuelta para saber que paso el tiempo.

Respecto al ultimo codigo que pasaste recorda que cada ciclo de reloj esta decrementando, ocurre que no podes preguntar por que valor posee el contador por que no sabes cuantos ciclos durarian las instrucciones adentro del if, haciendo invalido el "tiempo", o invalido la forma de tomar el tiempo ( es mejor acostumbrarse como te digo para que pasar a una interrupcion sea solo un paso).
Si le cargas 14, quiere decir que en 15 ciclos de reloj el tiempo se terminaria. Nuevamente si suponemos un reloj de 10Mhz que entre al procesador, tenemos que:

Código: [Seleccionar]
14 - 0.0us
13 - 0.1us
12 - 0.2us
11 - 0.3us
10 - 0.4us
9 - 0.5us
8 - 0.6us
7 - 0.7us
6 - 0.8us
5 - 0.9us
4 - 1.0us
3 - 1.1us
2 - 1.2us
1 - 1.3us
0 - 1.4us
14 - 1.5us - Aca el bit COUNTFLAG se pone en 1, el valor del contador actual toma el valor del LOAD para comenzar todo de nuevo

Si te pensas que una instruccion puede tardar desde 1 a 2 o 3 ciclos de reloj, te das cuenta que 15 ciclos es MUY poco.

Tampoco tenes la libertad de hacer el tiempo que quieras. Como te dije antes el MAXIMO que podes cargar en LOAD es de 16 millones y algo mas, en hexadecimal es 0xFFFFFF (en decimal lo puse en el anterior respuesta).
Si te pones a pensar si tenes un reloj de 10Mhz y cargas 10.000.000-1 en LOAD tendrias 1 segundo. Esto podria ser factible hasta un reloj de 16Mhz, si no me equivoco ese micro llega hasta los 48Mhz, Por lo tanto no podes poner ese valor tan grande. Y lo mas seguro es que puedas hacer un timer de 0.25s ( 48.000.000 / 4 = Cargar 12.000.000-1 )

PD: Respecto a los SystickConfig y las funciones del NVIC, no serian necesarias ponerlas por ahora. Por eso las quite.
el SystickConfig, hace lo mismo que hiciste antes, lo de cargar los registros LOAD/VAL y CTRL.
Lo del NVIC modifica otros registros mas pero esto es para interrupciones.

Desconectado mojo

  • PIC10
  • *
  • Mensajes: 23
Re:SysTick en KL46z256VLL4
« Respuesta #6 en: 08 de Diciembre de 2016, 13:31:00 »
Hola, muchas gracias por tu ayuda.

Creo que ya me quedó claro como funciona, en todo caso, encontré una solución que quizás se adapte mejor a las necesidades de mi problema, a ver que te parece:

Código: C
  1. extern __IO uint32_t time1;
  2.  
  3. int main (void)
  4. {
  5.   SysTick_Config(SystemCoreClock / 1000); //1ms
  6.   time1 = 1000;
  7.  
  8.   while (1)
  9.   {
  10.     if (time1 == 0)
  11.     {
  12.       toggleLed();
  13.       time1 = 1000;
  14.     }
  15.   }
  16. }

Teniendo la función SysTick_Handler definida en otro fichero:

Código: C
  1. __IO uint32_t time1;
  2.  
  3. void SysTick_Handler (void)
  4. {
  5.   if (time1 != 0)
  6.   {
  7.     time1 --;
  8.   }
  9. }

¿Qué te parece? Lo malo que le veo es que tengo que volver a poner manualmente el valor de "time1" cada vez que llega a 0...

Pero para mi problema es perfecto, porque cada vez que se llega a 0, quiero que "time1" cambie de valor: un semáforo no está en rojo el mismo tiempo que en amarillo ;)

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:SysTick en KL46z256VLL4
« Respuesta #7 en: 08 de Diciembre de 2016, 18:40:18 »
Esta bien esa solucion y es lo que generalmente se usa, lo que si yo no usaria _IO. Que es un tipo especial que suele usar las librerias, simplemente usaria "volatile" quedandote:

Código: C
  1. volatile uint32_t time1;
  2.  
  3. //y
  4.  
  5. extern volatile uint32_t time1;


Desconectado mojo

  • PIC10
  • *
  • Mensajes: 23
Re:SysTick en KL46z256VLL4
« Respuesta #8 en: 10 de Diciembre de 2016, 10:37:31 »
Vale, haré así entonces.
Gracias, saludos. :-/

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:SysTick en KL46z256VLL4
« Respuesta #9 en: 10 de Diciembre de 2016, 10:53:39 »
Recien me doy cuenta de una cosa... tendrias que hacer un cambio mas...

Código: C
  1. extern volatile bool PasoTiempo;
  2.  
  3. int main (void)
  4. {
  5.   SysTick_Config(SystemCoreClock / 1000); //1ms
  6.  
  7.  
  8.   while (1)
  9.   {
  10.     if (PasoTiempo == true)
  11.     {
  12.       toggleLed();
  13.       PasoTiempo = false;
  14.     }
  15.   }
  16. }

Código: C
  1. static uint32_t time1 = 1000;
  2. volatile bool PasoTiempo;
  3.  
  4. void SysTick_Handler (void)
  5. {
  6.   time1--;
  7.   if (time1 == 0)
  8.   {
  9.     time1 = 1000;
  10.     if(PasoTiempo == true )       //Este if se puede omitir que seria la parte de deteccion de error
  11.     {
  12.          // Aca error, por que nadie puso a PasoTiempo en false y ya hay que ponerlo de nuevo en true
  13.      }
  14.      else
  15.      {
  16.           PasoTiempo = true;        // Esta instruccion debe ir que es la que indica que paso el tiempo y da paso a el main.
  17.       }
  18.   }
  19. }

Como veras ahora no dependo de encontrar a time en 0, si mi main tarda mas de lo que debe todavia puedo seguir teniendo una base confiable. Ya que me guio con la otra variable.
El static, es para que el simbolo no salga del archivo en cuestion ( por si pones las interrupciones en otro lado ), Y esto esto que usualmente se usa :P
« Última modificación: 10 de Diciembre de 2016, 10:55:59 por KILLERJC »

Desconectado mojo

  • PIC10
  • *
  • Mensajes: 23
Re:SysTick en KL46z256VLL4
« Respuesta #10 en: 14 de Diciembre de 2016, 16:31:44 »
Lo acabo de leer justo ahora. Gracias por tu ayuda!

Saludos :-)


 

anything