Problema con bus I2C (no se genera SCL)

Lo primero de todo saludos a toda la comunidad. Como ya escribí en el foro de bienvenida me llamo Juan y actualmente estoy finalizando mis estudios de telemática en la universidad de Jaén.

Actualmente estoy desarrollando el proyecto final de carrera y éste es el motivo por el que escribo, llevo unas semanas "atrancado" con el bus I2C y necesito ayuda de gente que haya trabajado con el.

La verdad es que yo nunca había trabajado con este protocolo hasta que empecé este proyecto, por lo que soy un novato en este aspecto.

Paso a detallar mi problema:

Estoy intentando comunicar un MCU (concretamente estoy utilizando la Demo board STM8L DISCOVERY de STMicroelectronics) con el ADC de Microchip MCP3424, ya que el ADC que incorpora el MCU tiene una resolución de 12 bits y necesisto una resolución mayor, que me la da el MCP3424 ya que éste puede alcanzar una resolución de hasta 18 bits.
Mi intención ahora mismo es solamente comunicarme con el ADC y leer sus entradas.

Siguiendo el protocolo I2C he programado una serie de funciones (el lenguaje de programación es c++) para poder realizar la comunicación; para ello he empleado tanto las bibliotecas que se me proporcionan en la página de STMicroelectronics como el manual para la familia de microcontroladores que estoy empleando (stm8l15x).

Las funciones son muy básicas, una para inicializar los puertos SCL y SDA como drenador abierto y el reloj así como el bus I2C, otra que sirve para comenzar la comunicación, otra para escribir un byte en el ADC otra para leer del ADC y una última para generar la condición de STOP.

Bien mi problema surge en la función que empieza la comunicación I2C; en ésta, realizo las siguientes operaciones:

Compruebo que el bus está libre, genero la condición de start y compruebo que se envía correctamente, indico la dirección del esclavo y el modo de operación (lectura o escritura) y espero asentimiento por parte del esclavo.

En resumen sería: void I2C_start (I2C_Typedef* I2Cx, unit8_t address, unit8_t direction)

Dónde los parámetros serían I2Cx (Nuestro dispositivo I2C), address (la dirección de 7 bits) y direction (modo de operación lectura o escritura).

Mi problema es que compruebo correctamente que el bus está libre, genera la condición de start y envía la dirección del esclavo en modo escritura pero no recibo asentimiento por parte del esclavo.

Cuando me surgió este problema, medí con el osciloscopio las señales SCL y SDA y lo que ocurre es que cuando se genera la condición de start SDA cae a nivel bajo ( como dicta el protocolo), pero SCL también cae a nivel bajo y permanece ahí o, lo que es lo mismo, no se genera señal de reloj.

He probado a bajar la frecuencia en el bus, a subirla, y también a hacer un general call también con resultados negativos. También he comprobado que el chip no está estropeado ya que he probado varios y me pasa lo mismo, además, uno de los que he probado se a utilizado en otro proyecto y funciona correctamente.

La verdad es que estoy desesperado porque llevo tres semanas dándole vueltas y no consigo avanzar si alguien pudiera echarme un cable estaría muy agradecido.

Para finalizar pido disculpas por la parrafada, mi intención era detallar mi problema lo mas concretamente posible, pero creo que se me fue de las manos y me extendí demasiado.

Saludos,

Juan Cristóbal
 
Hola, gracias por haber dedicado vuestro tiempo a leer mi duda y la velocidad de respuesta.

En respuesta a su respuesta, efectivamente, he colocado las resistencias pull-up en ambas líneas, ambas con un valor de 5K6 ya que, según el manual del MCP3424 tengo que colocar dichas resistencias con un valor de entre 5K y 10K para los modos de operación estándar y rápido (100 kHz y 400 KHz), pues, siempre según el manual, cuando configure el ADC (si es que lo consigo) para que opere con una resolución de 18 bit, necesito una velocidad de Bus baja.

De todas formas de ésto último que he dicho no hagáis caso puesto que lo primero que tengo que hacer es establecer comunicación, no se cual puede ser la causa de que no tenga señal de reloj...
 
La mayoria de los microcontroladores tienen varias funciones en un mismo puerto de E/S, en ocaciones hay que configurar la direccion (si es entrada o salida) antes de usar la otra funcion, quizas no configuraste al pin SCL como salida y por eso no obtienes la salida.
¿Haz probado con algun otro dispositivo?, para descartar que sea el ADC el qu esta fallando.
 
No se entiende lo que pusiste, pareceria que SDA y SCL bajan juntas y por otra parte escribiste que se da la condicion de start, pero ambas cosas no pueden ser ciertas.

La condicion de start es que SDA baje mientras SCL se mantiene en alto.
 
He configurado los pines SCL y SDA en modo de operación drenador abierto, alta impedancia, alto nivel (salida push-pull).

Voy a seguir tu sugerencia de probar con otro dispositivo, he conseguido agenciarme una segunda demo board stm8l discovery y voy a ver si soy capaz de comunicar las 2 una como maestro y otra como esclavo haber si lo consigo y comento los resultados.



No se entiende lo que pusiste, pareceria que SDA y SCL bajan juntas y por otra parte escribiste que se da la condicion de start, pero ambas cosas no pueden ser ciertas.

La condicion de start es que SDA baje mientras SCL se mantiene en alto.

Disculpa si no me he explicado bien, como dije no controlo muy bien el protocolo I2C, lo que quiero decir es que en primer lugar ejecuté el código y vi que se quedaba en un bucle infinito al no recibir asentimiento del esclavo al envío de la dirección; para explicarme mejor te posteo el código de la función I2C_start:

void I2C_start (I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction){

while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)); //Esperamos hasta que el MCP3424 no esté ocupado
I2C_GenerateSTART(I2Cx, ENABLE); //bit START=1
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)); //Esperamos el asentimiento del esclavo a la condición de arranque
I2C_Send7bitAddress(I2Cx, address, direction); //Enviamos la dirección del MCP3424 además del modo de operación (escritura o lectura)

//Esperamos el asentimiento del esclavo; tenemos dos situaciones posibles:

if(direction == I2C_Direction_Transmitter){
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //Esperamos ack si estamos actuando en modo escrituta
}
else if(direction == I2C_Direction_Receiver){
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //Esperamos ack si estamos actuando en modo lectura
}
}

Puse un punto de ruptura en la parte del código donde llamo a esta función y donde se queda es esperando ack (el evento I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED).

Por lo cual mi siguiente paso fue ejecutar esta función paso a paso y, midiendo con el osciloscopio, cuando ejecuto la función "2C_GenerateSTART(I2Cx, ENABLE)", SDA pasa de alto a bajo y SCL también y se mantiene ahí sin generar reloj, luego ejectuo la función que realiza el envío la dirección mas el modo de operación y, se queda esperando asentimiento, en el registro de estado de I2C además me indica este error (falló ACK).

No se si me he explicado mejor, de no ser así por favor indiquemelo para intentar exponer otra explicación más clara.

PD: Gracias por su interés
 
Última edición:
Hola,

Bueno, si la funcion baja a las dos juntas, SCL y SDA... pues creo que hay un problema en la funcion. SDA tiene que bajar primero, luego SCL

Y lo otro que no entiendo es cual es la condicion de acknowledge del esclavo al start... no conozco nada asi a nivel del protocolo, simplemente el master genera el start y despues comienza a enviar los bits.

El esclavo envia un acknowledge solo despues de haberse transmitido al bus la direccion y el sentido de escritura. Por lo menos asi lo conozco yo. Despues hay otro acknowledge al final de los datos. Pero creo que no hay acknowledge al start.
 
Hola,

Bueno, si la funcion baja a las dos juntas, SCL y SDA... pues creo que hay un problema en la funcion. SDA tiene que bajar primero, luego SCL

Y lo otro que no entiendo es cual es la condicion de acknowledge del esclavo al start... no conozco nada asi a nivel del protocolo, simplemente el master genera el start y despues comienza a enviar los bits.

El esclavo envia un acknowledge solo despues de haberse transmitido al bus la direccion y el sentido de escritura. Por lo menos asi lo conozco yo. Despues hay otro acknowledge al final de los datos. Pero creo que no hay acknowledge al start.

Lo programo así porque la librería de st que incorpora las funciones I2C me dice lo siguiente (cito textualmente):

After sending the START condition (I2C_GenerateSTART() function) the master has wait for this event. It means that the Start condition has been correctly relased on the I2C bus (the bus is free, no other devices is communicating)

//Edito//

El evento al que me refiero es I2C_EVENT_MASTER_MODE_SELECT

//Fin de edición//

Mientras escribo esto me estoy dando cuenta de que esto no es ningun ack del esclavo, sino la comprobación de que el bus sigue libre, mil disculpas.

Estoy de acuerdo contigo de que el ack solo se envía despues del byte el cual incluye la condición de start, los 7 bits de dirección y el de R/W.

La función I2C_GenerateSTART() hace lo siguiente, según lo expuesto en la biblioteca:

Generates I2C comunication START condition, luego estoy siguiendo los pasos que me dice el fabricante, aunque no se si bien.

Reiterando lo que dije anteriormente la respuesta a ese I2C_GenerateSTART() es la puesta a nivel bajo de SDA y SCL y el evento que indico se produce correctamente, o eso creo...

Disculpa mi ignorancia, hablo siempre desde la suposición.
 
Última edición:
Yo se que suena feo decir que una funcion del fabricante no anda... pero no anda. Si es culpa de la funcion o de otra cosa, no lo se. Dicho sea de paso pusiste que configuras los pines como open drain, alta impedancia y push-pull... y son tres condiciones excluyentes una de la otra, por lo menos si hablamos de configuracion de salidas. Para I2C necesitas salidas open drain.

Por otra parte decis que esas tarjetas ya fueron usadas... trata de hablar con quien las uso y ver que codigo y que configuracion uso.

Ponete a leer bastante, si vas a hacer debugging de un protocolo lo tenes que conocer bien. Suerte.
 
Yo se que suena feo decir que una funcion del fabricante no anda... pero no anda. Si es culpa de la funcion o de otra cosa, no lo se. Dicho sea de paso pusiste que configuras los pines como open drain, alta impedancia y push-pull... y son tres condiciones excluyentes una de la otra, por lo menos si hablamos de configuracion de salidas. Para I2C necesitas salidas open drain.

Por otra parte decis que esas tarjetas ya fueron usadas... trata de hablar con quien las uso y ver que codigo y que configuracion uso.

Ponete a leer bastante, si vas a hacer debugging de un protocolo lo tenes que conocer bien. Suerte.

El MCU no lo ha usado nadie, lo que si se ha usado ha sido el MCP3424 pero fue usado con un Arduino y las bibliotecas son diferentes.

La configuración de los pines también las hago con las recomendaciones del fabricante:

GPIO_Mode_Out_OD_HiZ_Fast // Output open-drain, high-impedance level, 10 Mhz

Lo mismo no me estoy explicando bien, le pido disculpas.

Ya pregunté a la persona que uso el ADC con el Arduino y no supo contestarme a mi duda ya que los MCU funcionan de forma diferente.

De todas formas muchas gracias, seguiré investigando haber si consigo sacar algo en claro.

Saludos
 
Última edición:
De ultima y ante la duda, I2C es un protocolo relativamente simple.

En vez de usar funciones que no te estan funcionando, hace tus propias funciones aunque sea para demostrar la comunicacion a bajisima velocidad e ir aprendiendo el protocolo.

Al final son dos bits nada mas, prenderlos y apagarlos. Busca por la red, hay centenares de proyectos funcionando, una vez que le tomes la mano te va a ir pareciendo una pavada.
 
Las funciones de inicialización y la de START, que son las que he logrado ejecutar hasta ahora, mueven a los registros correspondientes el código de configuración haciendo exactamente lo que dicta el fabricante, lo he comprobado con el manual del MCU en la mano, podría programar yo las funciones moviendo los bits correspondientes a los registros pero, humildemente, creo que estaría haciendo lo mismo solo que cambiando el nombre a las funciones, pues éstas son muy simples.

No quiero parecer prepotente, le contesto esto con toda humildad y desde mi condición de principiante, lo que ocurre es que llevo ya un mes "atrancado con esto", por lo que he probado ya muchas cosas y me he empapado los manuales sin resultados.

Sin embargo y dándole la razón, la última solución sería programarlo yo todo desde 0, aunque quiero dejar
esto solo en un caso extremo ya que se me viene el tiempo encima.
 
Me refiero especificamente a la funcion que, para mi, no te esta andando. Anda y lee el protocolo de I2C y vas a ver que la funcion Start es imprescindible que sea SDA bajo con SCL alto, es una violacion al protocolo sincronico que se hizo para definir funciones especiales.

Algo en la configuracion de la funcion no esta bien o la funcion en si no anda (cosa que me pareceria rarisimo), pero hoy por hoy no estas poniendo start en el bus.

Otra cosa que podes hacer es buscar si el fabricante del chip tiene foros o soporte a los que puedas preguntarle.
 
Creo que tienes razón, cuando ejecuto I2C_GenerateSTART() se debería poner en el registro de control I2C_CR2 un 0x01, ya que el bit 0 de este registro es el que genera la condición de START, y esto no ocurre permanece a 0, esta claro que me esta fallando.

I2Cx->CR2 |= I2C_CR2_START, donde I2C_CR2_START está definido como 0x01, es decir me debería poner un 1 en el bit 0 de CR2 ¿no es así?, esto no sucede no se por qué.

Muchísimas gracias, estaba equivocado mi error no es que se generara la condición de START y bajara SCL sino que nunca llegaba a inicializarse esta condición. Por lo menos he dado un pequeño paso gracias a ti, ¿cuál podría ser el motivo de que no se asigne el valor al registro? La línea de código la ejecuta, pero el valor sigue siendo 0x00 en el registro I2C_CR2

// Edito

Comprobado, intento meter un "1" en el bit 0 del registro de control del que hablo sin usar la funcion y no me deja, ¿cuál podría ser el motivo? ¿problemas en la inicialización del bus?, también he omitido aquí las funciones de la librería y meto la configuración en los registros "manualmente" y sigue sin funcionar
 
Última edición:
Vamos a ver que me estoy liando y mucho, la condición de start si se envía porque después de ejecutar la condición de start he mirado el valor que hay en el registro de estado del I2C en el MCU y esta activo el bit de "condición de start generada"

Y SDA y SCL se me vienen las dos abajo.

Entiendo que el protocolo dice que SDA pasa a nivel bajo y SCL se mantiene en alto cuando se envía la condición de Start, pero es que ese es precisamente mi problema, envío la condición de start al bus I2C (y el envío se realiza, puesto que se me indica en el registro de estado del I2C del MCU que así es) y algún problema que no consigo detectar hace que SCL se venga abajo y no genere reloj, estoy muy agobiado la verdad...

He probado con oro dispositivo I2C, y tampoco me funciona, el problema debe ser de software, pero no lo veo...
 
Última edición:
Te entiendo pero vamos, animo!

Me parece que en esta comunidad hay pocos que te puedan ayudar con este problema que se me hace muy especifico del STM8. Me parece que lo mejor que podes hacer es buscar foros de STM en los que puedas poner tu codigo y gente que ya lo usa y lo conoce te puede ayudar.

Suerte...
 
Muchas gracias por tu interés,

En los foros de ST no recebo respuesta de nadie, estoy preguntando en un blog de una chavala que tiene un código con un STM34F4;

De cualquier forma creo que el problema debe estar en la inicialización del dispositivo, ya que he probado a no inicializar los pines scl y sda y hace lo mismo.

Seguiré investigando por ahí, cuando consiga resolver el problema (espero que lo consiga) posteare la solución por si alguien en un futuro tiene el mismo problema.

Saludos
 
No es normal

Si agregaste capacitores de SCL a masa, sacalos. Si no, proba reduciendo el valor de la resistencia de pullup de SCL.

Como reolviste el problema del reloj?
 
Atrás
Arriba