Hello this is what i'm trying without success : I want to develop a CircleOS application that will be able to use I2C1 port (on external connector). So I develop a piece of software to enable I2C1 and try to toggle I2C SCL (just to debug at beginning) => and I check I2C1 periperal (=> always @00) and I2C SCL port (no toggling).
/* Send the EEPROM's internal address to write to */ I2C_SendData(I2C1, ReadAddr);
I made a lot of trials : 1- I tryied to add another stm32f10x_conf.h with #define _I2C1 => no change when I launch New appli
2- I checked also on CircleOS files that I2C1 is not enabled and I built again the CircleOS witn #define _I2C1 and use cirle_primer2.edf in my new App => no change
Is it possible to manage I2C1 port with Circle OS on primer2 ? How to do that ?
Hello, many thanks for this suggestions. This help me to run I2C1 port.
Anyway, I success toggling I2C1 port using a "single" project. Configuring I2C1 port and Reading/Writing a EEPROM memory with I2C1 port (external connector).
But if I try to integrate the piece of code with Circle OS in an application CircleOS, Nothing happens on the I2C1 bus (IOB6/7). The port is well configured but not possible to toggle the IOs. All registers are well prepared ... No Start condition can be ran.
is Inserting the possibility to use I2C1 in Circle OS application possible or is there any conflit ?
as said before, I'm writing a program to be able to read/write an EEPROM on external port (using I2C1 bus). If I run the program alone (without any CircleOS environnement) all is ok, I success in reading the EEPROM ... My problem is when I try to insert the peace of program in a Circle OS application project. Last week, nothing happens, event if the I2C1 bus configuration was well done.
Trying to compare (in debug analysis) all the peripherals between the program running without (and OK) & with CircleOS (and HS) , I found that Disabling the FSMC RCC_AHBENR (value with CircleOS running 0x115) and changing it to 0x015, I success sending START condition, send 7 bits + RW + ACK on the bus. But there still have some problems :without CircleOS, I can read more than 4000 bytes without any problem (read byte + send ACK). But with Circle OS, I2C clock is running all the time, but SDA management seems to be every 300us. I think this is due to some interrupts, probably due to CircleOS firmware to check MEMS or Buttons or other user interrupt.
How can I solve this issue, or how can I know what are the activated interrupts , how can I desactivate its and re-activate it because I need to keep CircleOS functionnal for my application ?
When we worked on the audio routines, we encountered similar problems with I2C. The timer2 interrupt that was used to read the MEMS killed the I2C communication. It's why we included the I2C communication in the same interrupt handler, and we set at the highest level the priority of the TIMER2 irq. When didn't try to investigate further why this problem exists... We just applied the solution.
We had to place the I2C2 management in the timer2 interrupt routine because this interrupt handler (used originaly for the MEMS) generated problems in the I2C management. I assume that you are facing the same problem with I2C1, and you could apply the same solution: managing I2C1 in the timer2 interrupt routine. The solution could be: 1. In ypur application, you read the address of the timer2 interrupt routine (pointer to TIM2_IRQHandler), 2. You replace it by your own routine that first calls the original functions (don't forget to restore the original vector when you exit your application). The code could be as follows:
I assume the problems you have with I2C1 are the same we had with I2C2. I2C2 works fine when we include its driver into the Timer2 interrupt routine. Did you compare your code with the code we have for I2C2 ?
I have still not been able to use I2C1 in the timer2 interrupt routine. Using the example code given above, but My_TIM2_IRQHandler is never called! I am assuming in the example code above UTIL_GetIrqHandler(TIM2_IRQ_ID, My_TIM2_IRQHandler); should be SetIrqHandler.
I have a number of questions:
1. Is it definately Timer2 that is used for all I2C and MEMs management?
2. Was TIM2_IRQ_ID 0x00B0 just given as an example? I have found in stm32f10x_nvic.h that TIM2_IRQChannel ((u8) 0x1C) , should this be used instead?
3. Should I be configuring / enabling any NVIC in my application initialisation and using any NVIC in my application handler?
4. Is there another possible way of achieving I2C1 use (eg. from altering Circle OS source code). If so, is there any example code?
I suggest that you download the CircleOS source files (available in the section). To your questions:
1. Is it definately Timer2 that is used for all I2C and MEMs management?
Yes: TIM2_IRQHandler() calls MEMS_Handler() that calls MEMS_ReadOutXY() that manages both SPI (for the MEMS) and I2C2.
2. Was TIM2_IRQ_ID 0x00B0 just given as an example? I have found in stm32f10x_nvic.h that TIM2_IRQChannel ((u8) 0x1C) , should this be used instead?
The parameter is the address given in the table 53 of the latest reference manual. It is 0x0000_00B0. In CircleOS, the IRQ table is managed in RAM in order to modify the redirection to the interrupt handlers. The function UTIL_GetIrqHandler() is quite simple and just returns the 32-bit value find in the RAM at the specified address. UTIL_SetIrqHandler() allows to modify this value. UTIL_GetIrqHandler() must be used in order to restore the initial value when exiting the application.
3. Should I be configuring / enabling any NVIC
NVIC is already initialized by CircleOS. If you just redirect the Timer2 handler, you don't need any additional enabling. This interrupt is supposed to be already active (it handles the communication with the MEMS).
4. Is there another possible way of achieving I2C1 use (eg. from altering Circle OS source code). If so, is there any example code?
You could insert your code in CircleOS, but it is a pity... It would be much better to keep CircleOS as it is. The main reason is that you should write some code compatible with the future releases of CircleOS. Using the API guarantees this upward compatibility.
It seems I now have my own working TIM2 IRQ Handler (confirmed by changing a global variable), that restores the original TIM2 IRQ Handler upon exiting the application. However I am still experiencing the same problem as before. The application hangs during the I2C protocol when it reaches
The I2C protocol I am using is very similar to the 'MEMS_ReadMultByte' that of the REva STM32_MemsToLCD example, using the same STM32 Firmware Library functions but altered to read a single byte.
I have configured SCL and SDA to open drain and pulled the lines up with 4.7k resistors.
If anyone has achieved I2C1 operation on the expansion header and could help it would be VERY much appreciated!
I have included my code below, if anyone can suggest any alterations, or even further debugging ideas it would be fantastic!
Code:
/********************** (C) COPYRIGHT 2007-2009 RAISONANCE ********************
* File Name : Application.c
* Author :
* Date First Issued :
* Description : Circle_App CircleOS application template.
* Revision :
*******************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include "circle_api.h"
// The following should be the minimal CircleOS version needed by your application
#define NEEDEDVERSION "V 1.5"
/* Private variables ---------------------------------------------------------*/
#define TIM2_IRQ_ID 0x00B0
/* Private functions ---------------------------------------------------------*/
enum MENU_code MsgVersion(void);
/* Public variables ----------------------------------------------------------*/
const char Application_Name[8+1] = {"I2C Test"}; // Max 8 characters
char testmsg1[15] = "original";
char testmsg2[15] = "changed";
char *testmsg = testmsg1;
void (*original_TIM2_IRQHandler)(void) = 0;
void MyIrqHandler(void)
{
testmsg = testmsg2;
original_TIM2_IRQHandler();
// Handling of I2C1 (extension connector)
// Master generates START condition to start communication
I2C_GenerateSTART(I2C1, ENABLE);
// Test on EV5 and clear it
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// APPLICATION HANGS HERE***********************************************
// Send FPGA address for write
I2C_Send7bitAddress(I2C1, 0x20, I2C_Direction_Transmitter);
// Test on EV6 and clear it
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
// Clear EV6 by setting again the PE bit
I2C_Cmd(I2C1, ENABLE);
// Send the FPGA's internal address to write to (24,hex 0x18)
I2C_SendData(I2C1, 0x18);
// Test on EV8 and clear it
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// Send START condition a second time
I2C_GenerateSTART(I2C1, ENABLE);
// Test on EV5 and clear it
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// Send FPGA address for read
I2C_Send7bitAddress(I2C1, 0x20, I2C_Direction_Receiver);
// Test on EV6 and clear it
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
// For single byte data transfer - disable ACK before reading the data
I2C_AcknowledgeConfig(I2C1, DISABLE);
// Test on EV7 and clear it
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
// Read the data just received from the FPGA on I2C1
u8 ReceivedData;
ReceivedData = I2C_ReceiveData(I2C1);
// Send STOP condition on I2C1
I2C_GenerateSTOP(I2C1, ENABLE);
// Enable Acknowledgement to be ready for another reception
I2C_AcknowledgeConfig(I2C1, ENABLE);
DRAW_DisplayString(10, 60, ReceivedData, sizeof(ReceivedData));
}
/*******************************************************************************
* Function Name : Application_Ini
* Description : Initialization function of Circle_App. This function will
* be called only once by CircleOS.
* Input : None
* Return : MENU_CONTINUE_COMMAND
*******************************************************************************/
enum MENU_code Application_Ini ( void )
{
// Ensure that the current OS version is recent enough
if(strcmp(UTIL_GetVersion(), NEEDEDVERSION) < 0)
{
return MsgVersion();
}
// TODO: Write your application initialization function here.
// RCC SYSTEM CLOCK CONFIG ----------------------------------------------------/
{
ErrorStatus HSEStartUpStatus;
/* RCC system reset(for debug purpose) */
RCC_DeInit();
/* Enable HSE */
RCC_HSEConfig(RCC_HSE_ON);
/* Wait till HSE is ready */
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
/* HCLK = SYSCLK */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* PCLK2 = HCLK */
RCC_PCLK2Config(RCC_HCLK_Div2);
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config(RCC_HCLK_Div2);
/* Flash 2 wait state */
FLASH_SetLatency(FLASH_Latency_2);
/* Enable Prefetch Buffer */
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/* PLLCLK = 8MHz * 9 = 72 MHz */
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
/* Enable PLL */
RCC_PLLCmd(ENABLE);
/* Wait till PLL is ready */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* Wait till PLL is used as system clock source */
while(RCC_GetSYSCLKSource() != 0x08)
{
}
}
/* Enable GPIOs clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Enable I2C1 clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 , ENABLE);
}
// GPIO CONFIGURATION --------------------------------------------------------/
// Configure all GPIOB in open drain AF mode (for different I2C levels)
// and Initialise
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// I2C CONFIGURATION ---------------------------------------------------------/
// Enable I2C1 peripheral
I2C_Cmd(I2C1, ENABLE);
// Configure I2C
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //only effects fast mode
I2C_InitStructure.I2C_OwnAddress1 = 0xA0;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000;
// Initialise the I2C1 peripheral after enabling it
I2C_Init(I2C1, &I2C_InitStructure);
DRAW_DisplayString(10, 70, testmsg, 12);
original_TIM2_IRQHandler = UTIL_GetIrqHandler(TIM2_IRQ_ID);
UTIL_SetIrqHandler(TIM2_IRQ_ID, MyIrqHandler);
const char msg[] = "Init ok";
DRAW_DisplayString( 2, 95, msg, sizeof(msg)); // X, Y, string, length
return MENU_CONTINUE_COMMAND;
}
/*******************************************************************************
* Function Name : Application_Handler
* Description : Management of the Circle_App.
*
* Input : None
* Return : MENU_CONTINUE
*******************************************************************************/
enum MENU_code Application_Handler ( void )
{
DRAW_DisplayString(10, 40, testmsg, 12);
// Restore original TIM2 IRQ Handler
UTIL_SetIrqHandler(TIM2_IRQ_ID, original_TIM2_IRQHandler);
#if 1
if ( BUTTON_GetState() == BUTTON_PUSHED )
{
BUTTON_WaitForRelease();
return MENU_Quit();
}
#endif
// This routine will get called repeatedly by CircleOS, until we
// return MENU_LEAVE
return MENU_CONTINUE; // Returning MENU_LEAVE will quit to CircleOS
}
/*******************************************************************************
* Function Name : MsgVersion
* Description : Display the current CircleOS version and the version needed
* exit to main menu after 4 secondes
*
* Input : None
* Return : MENU_CONTINUE
*******************************************************************************/
enum MENU_code MsgVersion(void)
{
int hh,mm,ss,ss2;
DRAW_DisplayString(5,60,"CircleOS",17);
DRAW_DisplayString(80,60,UTIL_GetVersion(),3);
DRAW_DisplayString(5,34,NEEDEDVERSION,3);
DRAW_DisplayString(40,34," required",12);
RTC_GetTime(&hh,&mm,&ss);
ss = ss + 4; // 4 seconds
ss = ss%60;
do
{
RTC_GetTime(&hh,&mm,&ss2);
}
while (ss2 != ss); // do while < 4 seconds
return MENU_REFRESH;
}
I would suggest: 1. Do NOT restore the original irq handler before exiting definitely your application. 2. You should fix for the duration of your application the clocking (UTIL_GetPLL, UTIL_SetPLL...). 3. Comment out the call to the original handler (only for debugging): //original_TIM2_IRQHandler(); Theoritically, it does not change anything but it will reduce the overall duration of the interrupt.
Thanks for your suggestions, I have experimented with UTIL_GetPll and UTIL_SetPll to fix the clocking in my application, however there is no change in the I2C operation.
The problem still seems to be with while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
Viewing the GPIOB and I2C1 peripherals I can see that the gpio pins and i2c1 are initialised.
Looking at I2C1 Control Register 1 (CR1), it says there is START generation, no STOP generation, and acknowledge returned.
Looking at I2C1 Status Register 1 (SR1), no change occurs at any point during my application, and SB Start Bit (Master Mode) is greyed out with no start condition.
Both SCL and SDA are pulled high (to 3.3V with an I2C voltage conversion circuit), and there is no change to them when running the application. Would there be a problem with the 3.3V due to the Primer 2's 2.8V?
I still cannot see why a START condition cannot be sent and no signals sent on the extension connector pins!
From other forum threads I have seen that a number of people have encountered problems with I2C on the external connector with CircleOS, and each person has had similar results.
From my own tests, I have found that: - When using I2C1 on the external connector, the application hangs just after START generation, at while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); - When using I2C2 on the external connector, the application hangs after a 7 bit address has been sent ( following START generation), at while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
Has anyone been known to achieve I2C operation on the Primer 2 external connector? Has it been tested and verified by yourselves?
I have experimented with handling the I2C operation within a TIM2 interrupt as suggested but have had no luck, probably though lack of in-depth knowledge of the source code. Are there any plans to understand this problem more (perhaps for future Circle OS releases), or provide thorough example code to work around this bug? I feel this is holding back an otherwise excellent product!
I would like to continue to try and overcome this problem. When you created the Circle OS I2C operations was any further information required that was not found from the Data Sheet / Firmware Library / Errata, such as a more detailed document of the I2C peripherals?
The I2C1 on the extension card does not run because of sharing PB7 (I2C1_SDA) with the FSMC (FSMC_NADV) used for the LCD management.
So, the only solution is to temporarily unvalidate the FSMC before calling I2C functions. This can be done by this function, followed by a delay : RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, DISABLE); for(i=0; i<1000; i++);
Then call RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE); and the LCD will run again.
This fixes the START generation, but sometimes the program still stalls. So, we also must disable temporarily the TIM2 interrrupts (MEMS), with the instruction : TIM_Cmd( TIM2, DISABLE );
We tested it with the "M24C08_EEPROM" example from the ST Library, implemented as a CircleOS application, with success.
We will add functions to validate and unvalidate FSMC into the new CircleOS release.
Thank you for taking the time to help fix this problem.
I have applied your suggestions to my application, but have only made very slight progress.
Disabling the FSMC for the LCD before calling any I2C functions, and enabling it again once I2C operation has completed, produces 'start generated' in I2C1's Status Register 1 (SR1).
However, the application still hangs, this time at test on EV6, even with TIM2 Disabled. // Test on EV6 and clear it while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
At this point, Status Register 1 now reads 'Arbitration Lost Detected', 'No Acknowledge Failure'. The board that the I2C is connected to, however, has no I2C operation occuring for there to be Arbitration Lost. This was confirmed using a logic analyser.
Further use of the logic analyser showed that SDA never toggles, and is constantly held high by the 4.7k resistors. SCL however goes low when generating start, and high again when 'start' has been generated. From this it is seen that a true START condition never occurs if SDA never toggles!
These problems were encountered with I2C1, with SCL and SDA configured to AF_Open Drain. Testing the operation with I2C2 caused the program to hang at the test on EV6 also, but with No Arbitration Lost detected, Acknowledge Failure, and SCL low and SDA staying high (never toggling).
Would it be possible to view the Circle OS application that you got I2C working with? This would mean i could compare it to my own application.