Autor Tema: ampliacion memoria externa 23K256 dsPic para data logger  (Leído 458 veces)

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

Desconectado niuton

  • PIC10
  • *
  • Mensajes: 21
ampliacion memoria externa 23K256 dsPic para data logger
« en: 14 de Marzo de 2019, 18:18:42 »
Hola a todos, tengo una duda a ver si me podeis ayudar.

Estoy realizando un datalogger para almacenar 8 canales de un convertidor ADC de 16bit de una señal alterna voy a coger 256 muestras por ciclo (con una velocidad de muestreo de 78us) lo que equivaldría a 256muestras*8canales*16B = 32KB de memoria por ciclo si no me equivoco.  Además necesito ir almacenando en cada lectura del ADC los 8 canales (2Bytes/ canal) serían en total 16bites y esta escritura deberia ser en unos 70us.

Esta cantidad de memoria SRAM para almacenar variables no lo encontraré en ningún PIC ni dsPIC y yo necesito almacenar por lo menos 10 ciclos que sería un total de 320KB.

Se me ha ocurrido esta solución pero nunca he trabajado con esto y no se si de verdad seria la solución.

Necesitaría ampliar la memoria SRAM con una externa usando el puerto SPI por ejemplo 23K256 o 23LC1024 que soporta el relog a 20MHz.

1) 23LC1024 pone 128K*8bit eso quiere decir que la memoria es capaz de almacenar 128KB?, por lo que necesitaría 3 memorias para almacenar los 320KB, verdad?.

2) el bus a 20MHz creo que sería muy difícil de conseguir sin que se deteriore la señal de reloj, creéis que 10MHz es factible de conseguir?.

3) según leo en el datasheet la secuencia de escritura secuencial seria:

 

* escritura.png
(36.21 kB, 667x408 - visto 95 veces)


según entiendo el tiempo que tardaría en escribir sería una lectura de 8 canales a 16 bit

instruccion 8     ciclos
adrress      16   ciclos
16bites      128 ciclos
TOTAL        152 ciclos de reloj

si pongo el reloj a 5MHz tendría un tiempo de escritura de los 8 canales con 16bites de 30.4us es esto correcto?

4) sería posible ya que colocaría 3 memorias en paralelo SIN usar la el chip set, usar el hardware del SPI del dspic para una memoria, y emular 2 salidas SDO y 2 salidas SDI usando la misma señal de CLK que produce el hardware del SPI, vamos realizar la escritura simultanea en paralelo con una sola señal de reloj?.

Bueno después de esta parrafada espero que alguien me pueda iluminar el camino ya que no estoy muy seguro de lo que he dicho  :?.

gracias!!!!!

Desconectado remi04

  • PIC16
  • ***
  • Mensajes: 197
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #1 en: 14 de Marzo de 2019, 19:20:52 »
No entiendo por que no quieres usar los CS de las memorias.  Sería muchísimo más fácil de manejar que estar monitorizando sck e ir poniendo bit a bit los datos que van a las memorias a la misma vez que tienes que ir leyendo los que vienen.

 Por otro lado el clk del spi no está siempre activo, solo durante una operación de transferencia de su registro de desplazamiento, a no ser que lo pongas en frame mode, cosa que tampoco te conviene para esta aplicación.

    Yo usaría SDI, SDO, CLK del módulo en paralelo a todas las memorias.  Luego usa los tres bits de menor peso de un puerto cualquiera para los 3 CS y ese puerto luego lo corres 1 bit a la izquierda “ PORTX = PORTX << 1; “ cada vez que address desborde, es decir, que apunte a la última dirección de la memoria que estás escribiendo + 1.  Entonces pones address de nuevo a cero y de forma automática se comienza a escribir en la segunda memoria, luego la tercera.. 


   



 
« Última modificación: 14 de Marzo de 2019, 19:35:01 por remi04 »

Desconectado niuton

  • PIC10
  • *
  • Mensajes: 21
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #2 en: 15 de Marzo de 2019, 06:28:23 »
gracias por responder remi04,

la pregunta (4) me he liado  :? y no quería preguntar eso, quería preguntar:

4) sería posible  colocar dos memorias en paralelo, una para el MSB y otra para el LSB del ADC de esa forma creo dos memorias en paralelo para guardar los 16 bits, compartiendo CSy SCK y emulando 1 SDO y SDI para la 2 memoria.

Sería posible compartir SCK y montar en paralelo, de esta forma ganaria velocidad, no? seria una opción valida?.

Si es verdad el CLK solo funciona cuando se escribe o lee, por eso preguntar si se podia compartir el SCK que produce el hardware del dspic para dos memorias.

Llevas razón con lo del CS no pensé en ir desplazando el puerto gracias. Lo que no me quedaba claro es lo de las paginas en la memoria porque dice que esta dividido en 32Bytes por paginas y que tiene 1024 paginas.

Entonces tu me comentas que puedo estar escribiendo de forma secuencial y el puntero se ira actualizando solo hasta que se desborda en los 32KBytes o la direccion (7FFF) y yo no le tengo que indicar nada de las paginas verdad?

gracias!!

Desconectado remi04

  • PIC16
  • ***
  • Mensajes: 197
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #3 en: 15 de Marzo de 2019, 15:00:48 »
Hola. He estado viendo el datasheet de esa memoria, la 23LC1024.

  El tema de las páginas, lo que he visto es que tiene 32 páginas de 32 KB ( KiloBITS) no confundas con Kb (Kilobyte).   Eso son 32 x 32 = 1024 Kbits = 1 Mbit.

  De todas formas no te rayes con las paginas, más abajo verás por qué.

  Admite escritura secuencial desde el byte 0 al ultimo. Pero hay que activarlo, por defecto el modo es Byte. Tal y como el chip se alimenta arranca preparado para modo SPI (también admite SQI),  y arranca en modo byte a byte, esto es, que primero hay que poner CS = 0, luego hay que enviarle el comando de escritura = 0b00000010 seguido de los 24 bits del Address , y luego el byte a grabar y por ultimo CS = 1; y así para cualquier byte.

 De este modo puedes apuntar a cualquier byte sin importar en qué página esté, pero este modo salta a la vista que pierde un huevo de tiempo. Descartado.

  Luego tiene otro modo que es por pagina, este modo es secuencial, pones CS = 0, envias comando de escritura, la posicion de inicio y a partir de ahi empiezas a mandar byte a byte todos los que quieras. El chip internamente te los va ordenando consecutivamente, pero solo hasta agotar la pagina donde estés, una vez agotada, si sigues enviando bytes saltaría a la posicion cero de esa página y empieza a sobreescribirla.

  Y por ultimo está el modo que creo que te interesa: Secuencial.  Funciona exactamente igual que el anterior pero aqui ya no va grabando dentro de una página sino toda la memoria entera a partir de la posición indicada en Addres.


  Para implementar esto,  al dar alimentación y arrancar tu programa lo primero que tienes que hacer es configurar la memoria para que trabaje en este modo, (secuencial), para ello el datasheet indica que la memoria arranca en modo Standby y que tienes que poner CS = 0; y seguidamente CS = 1; (un pulso) con eso el chip despierta.

  Luego:
 Poner CS= 0;
   Enviar comando de escritura en el registro Mode (0b0000001) seguido de la configuración deseada que para activar el modo secuencial sería 0b01000000.
 Poner  CS = 1;

   Con eso ya tendrías activada la memoria en modo secuencial.

  Repite todas las operación con las otras dos memorias, no olvides el pulso al pin CS para despertar cada una de las memoria. También puedes mandar el pulso a las tres a la vez y luego activar también las tres a la vez poniendo a nivel bajo los tres CS’s, enviar el comando de configuración y los tres CS ‘s de nuevo a 1.  Como prefieras.


   No te voy a explicar con tramas de código por que desconozco el lenguaje que usas, pero el planteamiento sería:

   Como te dije, los tres CS de las tres memorias los pones a tres puertos del pic, los que quieras.

   Define esos puertos como CS1, CS2, y CS3 para acceder a ellos mas cómodamente.

    SDI,SDO,SCLK en paralelo entre el micro y las tres memorias.
    CS1 a CS de la memoria 1.
    CS2 a CS de la memoria 2.
    CS3 a CS de la memoria 3.

   Eso en cuanto a Hardware.

    Pon los tres CS´s como salida y arranca el programa poniendo los tres a nivel alto.

   Cada memoria tiene 128 Kb (Kbytes) por lo que para llevar el control de "por donde va la memoria" necesitaras 17 bits, es decir, creas una variable de 32 bits para usarla como contador.

   Haz un bucle, por ejemplo con un while, en ese bucle metes todas las rutinas de lectura del ADC.

   Si el modulo ADC de tu micro almacena el resultado en un solo registro de 16 bits, luego separalos tu en dos variables distintas, ten en byte alto en una variable de 8 bits (ADCH por ejemplo) y el byte bajo en otra variable (ADCL por ejemplo).

  Si el micro te da el resultado en dos registros de 8 bits entonces ya tienes este trabajo adelantado, no necesitas crear variables para el resultado.


    Lo que hay que hacer es lo siguiente:

  Te creas una variable global de tipo entero de 32 bits sin signo  (accesible desde cualquier parte del programa) que puedes llamar por ejemplo “byte_counter = 0;” y la inicializas a cero. Esta variable contendrá la cuenta del numero de bytes exactos que llevas escritos en las memorias.  Cada vez que envíes un byte a las memorias, una vez grabado esta variable se incrementará en una unidad.

  Crea tres banderas (variable de tipo bool mismamente) que se llamen por ejemplo (memory1_open = 0;), (memory2_open = 0;), (memory3_open = 0;) y las inicializas a cero las tres, mas abajo verás para qué son estas banderas.


 
  Ahora Haces una rutina que va a recibir byte a byte desde la rutina o bucle del ADC, leerá tu contador y en función de su valor seleccionará la memoria adecuada y así los datos irán a ella, por ejemplo:

    Si (byte_counter <= (es menor o igual) a 127999)   Y ADEMÁS  (memory1_open = 0)  {     // Se tienen que cumplir las dos condiciones.
                                                                                                                                  Pones CS1 = 0;
                                                                                                                                  Envias a la memoria 1 el comando de escritura (0b00000010) seguido del address que sería CERO, para empezar a escribir la memoria desde el byte cero.
                                                                                                                                  memory1_open = 1;            // Indicamos que la memoria ya está abierta y asi no se repita el envío del comando a la memoria.
                                                                                                                               }
 
 Todo lo anterior significa lo siguiente:  Si el byte que acabamos de enviar está comprendido entre 0 y 127999 (eso son los 128.000 bytes) significa que estamos escribiendo en la memoria 1, o vamos a escribir en la memoria 1. Y además, la bandera Memory open debe ser cero, eso significa que vamos a EMPEZAR a escribir en la primera memoria que aun está cerrada, (hay que enviarle el comando de escritura y la dirección de donde vamos a empezar a escribirla, normalmente cero.

  Entonces envías el comando a la memoria y la dirección de partida (cero) en tu caso y pones la bandera memory_open = 1.    ¿ Para qué?.    Para que la próxima vez que esta función sea llamada NO vuelva a enviarte el comando a la memoria, sino solo el byte en si.

Ya tienes abierta la primera memoria, a partir de ahora cada byte que le envíes se grabará el primero en la posición indicada y los siguientes en posiciones consecutivas. 

  Ahora entonces agregamos el segundo condicional, va a ser exactamente igual, pero para la segunda memoria:

 
 Si (byte_counter <= (es menor o igual) a 255999)   Y ADEMÁS  es >= (Mayor o igual a 128.000 Y ADEMÁS,  (memory2_open = 0)  {     // Se tienen que cumplir las TRES condiciones.
                                                                                                                                  Pones CS1= 1;   Desactivas la memoria anterior.
                                                                                                                                  Pones CS2 = 0;  Activas la segunda memoria.
                                                                                                                                  Envias a la memoria 2 el comando de escritura (0b00000010) seguido del address que de nuevo sería CERO, para empezar a escribir la memoria desde el byte cero.
                                                                                                                                  memory2_open = 1;          // Indicamos que la memoria ya está abierta y asi no se repita el envío del comando a la memoria.   
                                                                                                                               }


  Esto es exactamente igual que el caso anterior, pero aquí preguntas si el contador está por encima de 127.999 y por debajo de 256.000 y de ser así, ya sabes que esos datos tienen que ir a la memoria 2, entonces preguntas además si la memoria 2 está abierta, si no lo está (memory2_open = 0;) cierras la anterior, CS1 = 1; abres la segunda memoria, CS2 =0; le envías el comando, informas de memoria abierta memory2_open = 1; y nada más.


  Te quedaría hacer lo mismo, para la tercera memoria.

  Preguntas si el contador está a 256.000 o por encima. Aquí ya no hace falta que preguntes si está por debajo de 384000 que sería el limite de información total, por que ya no tienes mas memorias puestas. Solo pregunta si está por encima de 256.000 Y ADEMAS
 si la memoria 3 está abierta, igual que en los dos casos anteriores. No olvides cerrar la memoria 2 CS2 = 1;

    Como ultima pregunta añade si el contador está a 384000 o por encima y en ese caso suspende el envió de bytes por que si no lo suspendes te sobreescribirá esa memoria desde el principio y por supuesto pon CS3 = 1; para desactivar la memoria 3.

   Ahora manda al SPI a escribir tu byte de dato relevante.

 Y por ultimo la función acaba incrementando el contador byte_counter = byte_counter + 1;

  Fin de función o rutina de escrituras de byte.

  Tu bucle inicial cada vez que lea un resultado del ADC mandará a esta fucnión DOS veces seguidas, una para el byte alto y otra para el bajo o al revés, como tu lo prefieras.

   Tu bucle inicial debería interrumpirse cuando esta función indique que la memoria se ha llenado (La ultima pregunta de la función). Podrías hacer que la función devuelva 1 si puede seguir recibiendo datos o que devuelva cero cuando la memoria esté completa.


   Uff, que tostón...  Espero no liarte mas todavía.

  Es muyyy fácil lo que quieres hacer.

  Ah, y no tengas miedo de poner el SPI a 20 Mhz si tu pic te lo admite, esa memoria te los admite aunque si quieres seguridad déjalo en 15 Mhz. No importa que se estropee el square del SCL, la memoria tiene internamente su buffer preparado para amoldarse bien a lo que le llegue en el clock. como si es sinusoidal la señal, la sigue detectando bien.

 
 
   
                 
 
« Última modificación: 15 de Marzo de 2019, 17:38:37 por remi04 »

Desconectado niuton

  • PIC10
  • *
  • Mensajes: 21
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #4 en: 15 de Marzo de 2019, 17:08:37 »
uffffffffff mucha gracias por tu respuesta ta didáctica y claramente explicada  ((:-)) ((:-)) ((:-)).

La idea es usar un dsPic 30F3013 y usar C con Mplab, voy a digerir todo lo que me has puesto y en cuanto me lleguen las memorias me pongo a probarlas. yo habia usado I2C pero SPI nunca.

mientras que encontraba una solución a lo del almacenamiento estoy configurando el SPI para hacer pruebas y ver como se configura, las interrupciones para recibir, etc.

con esto que me pones veo que es muy facil aumentar la capacidad tan solo metes otra condición otra bandera y ya esta aumentada jeje.

por lo de la velocidad pues si le meteré caña a 20MHz que si lo soporta el uC.

De nuevo muchas gracias por esta información.

cuando consiga algún avance iré posteando como progresa.

Desconectado remi04

  • PIC16
  • ***
  • Mensajes: 197
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #5 en: 15 de Marzo de 2019, 19:41:45 »
Si ya has trabajado con i2c y no has tocado nunca SPI te recomiendo leer algún tuto sobre el SPI en si por que es un poco “raro” trabajando. 

 I2c es muchísimo más fácil pero SPI es muchísimo más veloz y eficaz.

 En SPI lo que tienes que tener claro es que funciona con un registro de desplazamiento único tanto para enviar como para recibir.   Ese registro corre con el clock que genera el pic siempre.  Por eso en SPI no existe un envío ni una recepción sino una transferencia o intercambio. Esto es así por que cuando envías algo automáticamente recibes algo de forma simultánea, aunque sea basura, pero lo tienes que recibir.  Del mismo modo, si quieres recibir algo tienes que enviar algo a cambio.

  Esto no te va a afectar a la grabación por que al grabar siempre pones cosas en el registro de desplazamiento del spi para que salga inmediatamente, pero cuando tengas que recibir algo, por ejemplo a la hora de leer la memoria como no tengas claro cómo funciona el spi te vas a volver loco.

 Por ejemplo para esta memoria,  si le pides que te devuelva lo que hay en la posición 6, tienes primero que enviar el comando de lectura y los 24 bits de la dirección, entonces la memoria queda lista para empezar a meterte los 8 bits de lo que hay en esa posición en tu registro de desplazamiento, para ello tienes que enviar otros 8 bits a cambio, aunque sea 0b00000000.  Asi generas 8 pulsos de reloj, la memoria ignora los 8 ceros y a la vez te va enviando los 8 bits que has pedido.

   Otra cosa es que los módulos SPI sin FIFO o con ellos requieren que constantemente hagas una lectura tras cada transferencia.  Si haces una transferencia y no lees el buffer , el módulo entra en OverBuffer y tienes que resetearlo. (Esto puede ser diferente en cada micro).

   Todo esto te lo aviso para que lo tengas en cuenta si nunca has tocado SPI.  Te va a parecer un poco raro su forma de operar al principio. Pero merece la pena. Es mucho mejor y más veloz que el I2c.

 Saludos.
 
 
 

Desconectado niuton

  • PIC10
  • *
  • Mensajes: 21
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #6 en: 16 de Marzo de 2019, 17:33:20 »
gracias de nuevo por la info ;-).

Ya me he visto el datasheet y estoy configurando el puerto para empezar a hacer pruebas, por desgracia con los preescaler que tiene el puerto SPI con 30MHz de cristal lo más cercano a 20MHz de la memoria es poner a 15MHz.

pondré la interrupción y aré pruebas la semana que viene.

muchas gracias.

Saludoss!!

Desconectado niuton

  • PIC10
  • *
  • Mensajes: 21
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #7 en: 17 de Marzo de 2019, 09:02:56 »
una duda si quisiera manejar más cantidad de memoria, por ejemplo 10 chips seria mas conveniente usar un decodificador para los CS en vez de usar diez pines.
Eso funcionaría verdad?

gracias

Desconectado remi04

  • PIC16
  • ***
  • Mensajes: 197
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #8 en: 17 de Marzo de 2019, 17:38:54 »
una duda si quisiera manejar más cantidad de memoria, por ejemplo 10 chips seria mas conveniente usar un decodificador para los CS en vez de usar diez pines.
Eso funcionaría verdad?

gracias
Si, es una buena opción usar decodificadores.

  También te puedes plantear la posibilidad de usar una tarjeta sd de 1 o 2 Gb. Se pueden comunicar por SPI.

Desconectado niuton

  • PIC10
  • *
  • Mensajes: 21
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #9 en: 18 de Marzo de 2019, 09:37:09 »
lo que no se es si el tiempo de escritura es rapido y me permitira escribir 16Bytes en menos de 70us.

buscare algo de información, gracias nuevamente

Desconectado niuton

  • PIC10
  • *
  • Mensajes: 21
Re:ampliacion memoria externa 23K256 dsPic para data logger
« Respuesta #10 en: 06 de Abril de 2019, 17:37:00 »
Hola de nuevo,

ya he estado practicando con el SPI del 30F3013 y he conseguido hacer funcionar un ADC que tenia por casa. El problema e que cuando quiero poner a funcionar la memoria la 23LC1024 no lo consigo, ya que lo que no termino de comprender bien en el datasheet es lo de que modo escoger en SPI .

segun la memoria, la comunicación SPI es:
 

* pi.png
(38.88 kB, 699x598 - visto 43 veces)


y la opcione que tengo en el dpic es:
 

* dp.png
(66.24 kB, 625x748 - visto 45 veces)


yo entiendo que deberia escoger en el dspic:
CKP = 1, CKE = 0, MP = 1;

pero lo estoy intentando con todas la opciones y no consigo leer el registro ni escribir y luego leer la memoria un simple byte.

a ver si me podéis aclarar.

el código que estoy utilizando es:

configuración dspic
Código: [Seleccionar]
// Configure SPI1 module to transmit 16 bit timer1 value in master mode
SPICONValue = FRAME_ENABLE_OFF & //FRMEN:  0 = Framed SPI support disabled
  FRAME_SYNC_OUTPUT & //SPIFSD: 0 = Frame sync pulse output (master)
  ENABLE_SDO_PIN & //DISSDO: 0 = SDOx pin is controlled by the module
  SPI_MODE16_OFF & //MODE16: 0 = Communication is byte-wide (8 bits)
  SPI_SMP_ON & //SMP:    1 = Input data sampled at middle of data output time
  SPI_CKE_OFF & //CKE:    0 = Serial output data changes on transition from clock state to active cstatelock  (w.r.t CKP - BIT:6) */
  SLAVE_ENABLE_OFF & //SSEN:   0 = SS pin not used by module. Pin controlled by port function
  CLK_POL_ACTIVE_HIGH &   //CKP:    1 = Idle state for clock is a low level; active state is a high level */
  MASTER_ENABLE_ON & //MSTEN:  1 = Master mode
  SEC_PRESCAL_6_1 & //SPRE<2:0>: Secondary Prescale 2:1
  PRI_PRESCAL_1_1; //PPRE<1:0> Primary Prescale 1:1

SPISTATValue = SPI_ENABLE & //SPIEN:   1 = Enables module and configures SCKx, SDOx, SDIx and SSx as serial port pins
   SPI_IDLE_CON & //SPISIDL: 0 = Continue module operation in Idle mode
   SPI_RX_OVFLOW_CLR; //SPIROV:  0 = No overflow has occurred. Clear receive overflow bit.

OpenSPI1(SPICONValue, SPISTATValue );


el programa que he realizado de prueba es:
Código: [Seleccionar]
LATBbits.LATB3 = 1;
__delay_us(1);
LATBbits.LATB3 = 0;
__delay_us(1);
LATBbits.LATB3 = 1;

while(1){


LATBbits.LATB3 = 0;
__delay_us(1);
result1 = SPI_WR(0x05); // union de 8bit 0x01 escritura en registro MODE
__delay_us(1);
LATBbits.LATB3 = 1;

printf("MODE: %d\n",result1);
__delay_ms(1);


LATBbits.LATB3 = 0;
__delay_us(1);
SPIWrite(0x01); // 8bit 0x01 escritura en registro MODE
SPIWrite(0x40); // 8bit 0x40 MODE secuencial
__delay_us(1);
LATBbits.LATB3 = 1;

__delay_ms(1);

LATBbits.LATB3 = 0;
__delay_us(1);
result1 = SPI_WR(0x05); // 8bit 0x01 escritura en registro MODE
__delay_us(1);
LATBbits.LATB3 = 1;

printf("MODE: %d\n",result1);
__delay_ms(1);


        LATBbits.LATB3 = 0;
__delay_us(1);
SPIWrite(0x02); // Instruccion escritura
SPIWrite(0x00); // Address 24bit
SPIWrite(0x00); // Address 24bit
SPIWrite(0x00); // Address 24bit

SPIWrite(0x01);
SPIWrite(0x02);


__delay_us(1);
LATBbits.LATB3 = 1;

printf("\nLectura\n");
LATBbits.LATB3 = 0;
__delay_us(1);
SPIWrite(0x03); // Instruccion escritura
SPIWrite(0x00); // Address 24bit
SPIWrite(0x00); // Address 24bit
SPIWrite(0x00); // Address 24bit


        result1=SPIRead();
result2=SPIRead();
__delay_us(1);
LATBbits.LATB3 = 1;

printf("%d %d\n",result1,result2);
        }

saludo.