реклама на сайте
подробности

 
 
2 страниц V  < 1 2  
Reply to this topicStart new topic
> STM32F030 проблема с I2C, Непонятное поведение I2C периферии контроллера
p_kav
сообщение Nov 9 2015, 09:13
Сообщение #16


Местный
***

Группа: Участник
Сообщений: 294
Регистрация: 5-08-14
Из: Ярославль
Пользователь №: 82 466



Цитата(AVI-crak @ Nov 9 2015, 14:03) *
Фига, режим отладки может и должен сбрасывать флаги при чтении. Потому как реакция флага на чтение/изменение - это выборка его адреса.
Стандартная фраза как в поликлинике - "я только спросить" - тут не канает.
Закрывайте ветки регистров в отладчике, для того чтобы они не опрашивались, и тогда, возможно, с пятого раза, и десяти перезапусках - оно и заработает.


У меня не было никаких проблем при работе с USART и открытым окном регистров.
И тут с I2C тоже пошагово всё работает с открытым окном регистров.
Видимо, там какой-то особый способ чтения памяти при отладке.

Цитата(vet @ Nov 9 2015, 14:10) *
Пытался работать с I2C-периферией и на STM32F, и на STM32L, на F она откровенно не работала, на L получше, но всё равно застревала в ошибочных состояниях, помогало только отключение периферии, остановка тактирования и перезапуск со сбросом.



Ну, раз несколько человеку подтверждают аппаратные глюки, значит остается только сделать софтовый I2C и не париться.
Go to the top of the page
 
+Quote Post
adnega
сообщение Nov 9 2015, 09:22
Сообщение #17


Гуру
******

Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702



Цитата(p_kav @ Nov 9 2015, 12:13) *
остается только сделать софтовый I2C и не париться.

Перед этим советую внимательно изучить ES в части ошибок I2C.
Там они есть))
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение Nov 9 2015, 10:21
Сообщение #18


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(p_kav @ Nov 9 2015, 09:47) *
Вот, наверное, так и поступлю, скорость мне не важна и памяти ещё много.
В STM32L1, кстати, I2C совсем другой, другие регистры. И там всё работает нормально. Видимо, осознали и починили.

Таки да. I2C в F1xx (с чего все начиналось) и затем перенятая в F2хх и F4xx - это полные джунгли. Собственно, вся ситуация усугблена тем, что готовить ACK|NACK надо на "лету", во время, когда байт выталкивается/втягивается. Из-за прерываний можно и не успеть, откуда и растут ноги необходимости запрета прерываний и прочие телодвижения. К моменту разработки F/L0xx ребята, наконец, выгнали из команды идиота, который намутил ранний I2C, и сделали нормально: почти полный автоматизм транзакций.

ТС не нужно сдаваться и колдовать над Sorf-I2C с ногодрыганием. Добейте аппаратный I2C. У меня есть плата с той же комбинацией - DS2484 и DS18B20, где все прекрасно работает. То есть, задача решаема.
Go to the top of the page
 
+Quote Post
p_kav
сообщение Nov 9 2015, 10:33
Сообщение #19


Местный
***

Группа: Участник
Сообщений: 294
Регистрация: 5-08-14
Из: Ярославль
Пользователь №: 82 466



Цитата(KnightIgor @ Nov 9 2015, 15:21) *
ТС не нужно сдаваться и колдовать над Sorf-I2C с ногодрыганием. Добейте аппаратный I2C. У меня есть плата с той же комбинацией - DS2484 и DS18B20, где все прекрасно работает. То есть, задача решаема.


А код основан на CMSIS с непосредственным обращением к регистрам?
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение Nov 9 2015, 10:50
Сообщение #20


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(p_kav @ Nov 9 2015, 11:33) *
А код основан на CMSIS с непосредственным обращением к регистрам?

На CMSIS, смесь регистров и STL.
Go to the top of the page
 
+Quote Post
p_kav
сообщение Nov 9 2015, 10:51
Сообщение #21


Местный
***

Группа: Участник
Сообщений: 294
Регистрация: 5-08-14
Из: Ярославль
Пользователь №: 82 466



А посмотреть можно? Или это коммерческая тайна?
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение Nov 9 2015, 11:29
Сообщение #22


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(p_kav @ Nov 9 2015, 11:51) *
А посмотреть можно? Или это коммерческая тайна?

Можно. Однако я не пишу, как в примерах, с огромным, каждый раз другим, main(). Общение с DS2484 не написано как эксклюзив пользования I2C, а есть у меня I2C драйвер (или скорее, две функции), через который происходит доступ к шине, а доступ к DS2484 ими пользуется. Многое завязано на мои наработанные библиотеки. Сложно будет вычленить код, чтобы он странслировался вне моей, как сейчас модно говорить, "экосистемы". Например, код работы с DS2484:
CODE

/*----------------------------------------------------------------------------
* Name: DS2484.c
* Purpose: I2C to 1-Wire IC driver
*
* Version: V1.00
*----------------------------------------------------------------------------
* History:
* V1.00 Initial Version
*----------------------------------------------------------------------------*/

#include "DS2484.h"
//
// General HAL:
//
#include <HAL_I2C.h>
#include <HAL_CMx.h>

/* ---------------------------------------------------------------------
*
* What I2C peripheral to use.
*/
const uint8_t DS_I2C_Peripheral __attribute__((weak, noinline)) = 0;

/* ---------------------------------------------------------------------
*
* Tries to recover from the stall state if any.
*/
__attribute__((weak))
void DS_Recover(void)
{
I2C_Recover(DS_I2C_Peripheral);
}
/* ---------------------------------------------------------------------
*
* Reads a currently pointed register from the device.
* If 'source' != 0, the source pointer code will be set before.
*
* Returns: -1 on error or [0..255] (read byte) on success.
*/
int DS_ReadData(uint8_t source)
{
int res = -1, wcnt = source ? 2 : 0;
unsigned char I2CBuf[] = {DS_DEVICE_ID, DS_SET_READPOINTER_COMMAND, source, 0};

if (ReadW_I2C(DS_I2C_Peripheral, I2CBuf, wcnt, 1) > 0)
res = I2CBuf[1 + wcnt];

return res;
}
/* ---------------------------------------------------------------------
*
* Sends a command eventually followed by a data byte to
* the device.
*
* Returns true on success.
*/
bool DS_SendCommand(uint8_t cmd, uint8_t dat)
{
int wcnt = 2;
unsigned char I2CBuf[] = {DS_DEVICE_ID, cmd, dat};

switch (cmd) {
case DS_DEVICE_RESET_COMMAND:
case DS_1WIRE_RESET_COMMAND:
case DS_1WIRE_READBYTE_COMMAND:

wcnt--; // no parameter
break;
}
return WriteW_I2C(DS_I2C_Peripheral, I2CBuf, wcnt) > 0;
}
/* ---------------------------------------------------------------------
*
* Weak callback invoked before and while DS_WaitFor1Wire()
* function waits for the 1-Wire bus got free.
*
* 'tmo' in ms when the function called before the loop (init)
* 'tmo' is 0 while calling in the loop (poll).
*
* The function to return false as soon as the timeout has expired.
* The weak implementation always returns true.
*/
__attribute__((weak))
bool DS_WaitingLoop(int tmo) {return true;}

/* ---------------------------------------------------------------------
*
* Waits as long as 1WB (bus busy) but not longer than
* given 'tmo' in ms.
*
* ATTENTION! Assumes, the internal register pointer in the device
* is set to the status register by the preceeding
* operation.
*
* Returns status bits ( >= 0) on success (1WB is 0 anyway)
* or < 0 if expired/any errors. Actually, the negative value
* is still keeping the last status register content in the lowest
* byte.
*/
int DS_WaitFor1Wire(int tmo)
{
int res; bool done = true;
DS_WaitingLoop(tmo); // start timeout
while((res = DS_ReadData(0)) >= 0 // status got
&& (res & DS_STATUS_1WB) // still busy
&& (done = DS_WaitingLoop(0))); // not expired yet
// tricky make negative while
// keeping the status bits on
return done ? res : res + INT32_MIN;
}
/* ---------------------------------------------------------------------
*
* Starts 1-Wire communication issueing 1-Wire RESET and waiting for
* the operation completed (1WS is no more busy and PPD bit is set due
* to some device on the bus).
*
* Returns the value of the status register in the lowest byte
* or -1 if an error occured.
*
* NOTE! - since PPD must be set for success, the result is always > 0
* in this case.
*/
int DS_Start1Wire(void)
{
int res = -1, val;
if (DS_SendCommand(DS_1WIRE_RESET_COMMAND, 0)
&& (val = DS_WaitFor1Wire(DS_TIMEOUT_PER_BYTE/2)) > 0
&& (val & DS_STATUS_PPD)) // a 1-Wire device is present
{
res = val;
}
return res;
}
/* ---------------------------------------------------------------------
*
*/
bool Init_DS2484(void)
{
return DS_SendCommand(DS_DEVICE_RESET_COMMAND, 0);
}
//------------------ EOF -------------------------------------------------------

В коде жирным выделены функции транзакций по I2C: ReadW_I2C() и WriteW_I2C().
В свою очередь, они реализованы в " драйвере":
CODE

/*----------------------------------------------------------------------------
* Name: stm32f0xx_iic.c
* Purpose: STM32F0xx I2C support
*
* Version: V1.00, tested on STM32F051
*----------------------------------------------------------------------------
* History:
* V1.00 Supports multibus access (now over two I2C busses).
* The specification in HAL_I2C.h has been also changed to
* add the 'port' parameter to the I/O functions.
*----------------------------------------------------------------------------*/
//
#include "stm32f0xx_proc.h"
#include "stm32f0xx_iic.h"
#include <stm32f0xx_dma.h>
#include <stm32f0xx_i2c.h>
#include <HAL_DWT.h>

// -----------------------------------------------------------------------------

#define GPIO_SCL1 {GPIOB, {GPIO_Pin_6, GPIO_Mode_AF, GPIO_Speed_10MHz, GPIO_OType_OD, GPIO_PuPd_UP}, Bit_SET, GPIO_AF_1}
#define GPIO_SDA1 {GPIOB, {GPIO_Pin_7, GPIO_Mode_AF, GPIO_Speed_10MHz, GPIO_OType_OD, GPIO_PuPd_UP}, Bit_SET, GPIO_AF_1}
#define GPIO_SCL1_GPIO GPIOB

#define GPIO_SCL2 {GPIOB, {GPIO_Pin_10, GPIO_Mode_AF, GPIO_Speed_10MHz, GPIO_OType_OD, GPIO_PuPd_UP}, Bit_SET, GPIO_AF_1}
#define GPIO_SDA2 {GPIOB, {GPIO_Pin_11, GPIO_Mode_AF, GPIO_Speed_10MHz, GPIO_OType_OD, GPIO_PuPd_UP}, Bit_SET, GPIO_AF_1}
#define GPIO_SCL2_GPIO GPIOB

// -----------------------------------------------------------------------------
//
// I2C access control block type
//
typedef struct i2c_control {

I2C_TypeDef *i2c; // pointer to the peripheral
RCC_PCLK_TypeDef rcc[2]; // clock init structures for I2C and AGFIO
GPIO_PIN_TypeDef lines[2], // SCL/SDA lines init structures
gpio; // SCL as GPIO init structure
DMA_Channel_TypeDef *rx_DMAChannel, // pointer the DMA channel servicing RX
*tx_DMAChannel; // pointer the DMA channel servicing TX
int rate, // I2C rate
wcnt, // counter of bytes to write
rcnt, // counter of bytes to read
icnt, // counter of all bytes
indx, // current index in the exchange buffer
code; // return code
char complete, // complete flag
errorflag, // error flag
guard; // I2C stall guard flag
unsigned char *ptr; // exchange buffer pointer
I2C_CallBackType cb; // call back pointer

} I2C_Control_Type;

// -----------------------------------------------------------------------------
//
// Constant structures
//
#if (USE_I2C_TX_DMA1 || USE_I2C_RX_DMA1)
static const RCC_PCLK_TypeDef
DMA1Clocks[] = {RCC_DMA1};
#endif

static const
NVIC_InitTypeDef
NVIC_I2CInitStructure[] = {
{
I2C1_IRQn,
I2C_IT_Priority,
ENABLE
},
{
I2C2_IRQn,
I2C_IT_Priority,
ENABLE
}
};
// -----------------------------------------------------------------------------
//
// I2C access control blocks for I2C1 and I2C2
//
static I2C_Control_Type
control[] = {{I2C1, RCC_I2C1,
{GPIO_SCL1, GPIO_SDA1}, GPIO_SCL1,
DMA1_Channel3, DMA1_Channel2,
},
{I2C2, RCC_I2C2,
{GPIO_SCL2, GPIO_SDA2}, GPIO_SCL2,
DMA1_Channel5, DMA1_Channel4,
},
};

// -- USEFULL LOCAL MACROS -----------------------------------------------------

#define BITS_ONE_BYTE 9

/* INIT_I2C_FLAGS:
1). Reset flags and pointers
2). Assume Buffer Index = 1
3). Clear possible RxNE by reading RXDR out
*/
#define INIT_I2C_FLAGS© { c->code = \
c->complete = \
c->errorflag = 0; \
c->indx = 1; \
I2C_GET_DR(c->i2c);}

#define I2C_FINISH_CALLBACK(n, c, e) { if (c->cb) \
c->cb(n, e);}
#define I2C_IS_BUSY(n) I2C_Is_Busy(n)
#define I2C_ISSUE_START(d) I2C_START(d);
#define I2C_ISSUE_STOP(d) I2C_STOP(d);

// -- LOCAL PROCEDURES ---------------------------------------------------------

// -----------------------------------------------------------------------------
//
// Implements the initialization of the expire counter for SB bit timeout
// and returns its value.
//
static int32_t I2C_SBTimeoutInit(uint8_t port, uint16_t bits)
{
int32_t us = bits*(1000000/control[port].rate + 1);
return DWT_NextTP(us); // time point ahead
}
// -----------------------------------------------------------------------------
//
// Implements the modification and comparision of the expire counter for
// SB bit timeout. This function is called out of the loop. It should
// return any value >0 if the timeout has expired.
//
static uint8_t I2C_SBTimeoutExpired(int32_t *tp)
{
return !DWT_Compare(*tp);
}
// -----------------------------------------------------------------------------
//
// Makes the main initilization of the I2C peripheral
// (and its assigned DMA channels if any in use).
// If the input parameter >0 (TRUE), forces the
// peripheral software reset before.
//
static void ReInit_I2C(uint8_t port, uint8_t full)
{
I2C_TypeDef *i2c = control[port].i2c;
I2C_InitTypeDef
I2C_InitStruct = {
0,
I2C_AnalogFilter_Enable,
0,
I2C_Mode_I2C,
0,
I2C_Ack_Enable,
I2C_AcknowledgedAddress_7bit
};
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
switch (control[port].rate/100000) {

case 0:
case 1: // around 100kHz
I2C_InitStruct.I2C_Timing =
(RCC_Clocks.I2C1CLK_Frequency/4000000 - 1) << 28 |
0x0042 << 16 | // the constants: see doc
0x0F13;
break;

case 3:
case 4: // around 400kHz
I2C_InitStruct.I2C_Timing =
(RCC_Clocks.I2C1CLK_Frequency/8000000 - 1) << 28 |
0x0033 << 16 | // the constants: see doc
0x0309;
break;
}
RCC_Configuration (control[port].rcc, sizeof(control[port].rcc[0]));
if (full)
{
CLEAR_CR1(i2c, ~(0));
}
I2C_INIT(i2c, I2C_InitStruct); // (sets PE, too)
IRQ_INIT(NVIC_I2CInitStructure[port]);
GPIO_Configuration(control[port].lines, sizeof(control[port].lines));

#if (USE_I2C_RX_DMA1)
{
DMA_InitTypeDef
DMA_InitRStruct = {
(uint32_t)control[port].i2c + offsetof(I2C_TypeDef, RXDR),
NULL, // DMA_MemoryBaseAddr, TBD
DMA_DIR_PeripheralSRC, // DMA_DIR
0, // DMA_BufferSize, TBD
DMA_PeripheralInc_Disable, // DMA_PeripheralInc
DMA_MemoryInc_Enable, // DMA_MemoryInc
DMA_PeripheralDataSize_Byte,// DMA_PeripheralDataSize
DMA_MemoryDataSize_Byte, // DMA_MemoryDataSize
DMA_Mode_Normal, // DMA_Mode
DMA_Priority_Medium, // DMA_Priority
DMA_M2M_Disable // DMA_M2M
};
DMA_INIT(control[port].rx_DMAChannel, DMA_InitRStruct);
}
I2C_DMACmd(i2c, I2C_DMAReq_Rx, ENABLE);
#endif

#if (USE_I2C_TX_DMA1)
{
DMA_InitTypeDef
DMA_InitTStruct = {
(uint32_t)control[port].i2c + offsetof(I2C_TypeDef, TXDR),
NULL, // DMA_MemoryBaseAddr, TBD
DMA_DIR_PeripheralDST, // DMA_DIR
0, // DMA_BufferSize, TBD
DMA_PeripheralInc_Disable, // DMA_PeripheralInc
DMA_MemoryInc_Enable, // DMA_MemoryInc
DMA_PeripheralDataSize_Byte,// DMA_PeripheralDataSize
DMA_MemoryDataSize_Byte, // DMA_MemoryDataSize
DMA_Mode_Normal, // DMA_Mode
DMA_Priority_Medium, // DMA_Priority
DMA_M2M_Disable // DMA_M2M
};
DMA_INIT(control[port].tx_DMAChannel, DMA_InitTStruct);
}
I2C_DMACmd(i2c, I2C_DMAReq_Tx, ENABLE);
#endif
}
// -----------------------------------------------------------------------------
//
// Function to recover from fatal bus/slave/I2C peripheral states mentioned in
// Errata.
//
// 1). Tries to recover from slave stall by issue of 11 clocks.
// For this purpose the SCL line is temporary reconfigured and
// driven as a GPIO.
// 2). Makes reset and full reinit of the I2C peripheral.
//
void I2C_Recover(uint8_t port)
{
uint8_t pulses = 2*11;
I2C_Control_Type *ctrl = &control[port];
I2C_TypeDef *i2c = ctrl->i2c;

#if (USE_I2C_RX_DMA1)

DMA_STOP(ctrl->rx_DMAChannel);
// DMA_Cmd(ctrl->rx_DMAChannel, DISABLE);
#endif
#if (USE_I2C_TX_DMA1)

DMA_STOP(ctrl->tx_DMAChannel);
// DMA_Cmd(ctrl->tx_DMAChannel, DISABLE);
#endif
I2C_PE_CLEAR(i2c); // software reset of I2C

GPIO_Configuration(&ctrl->gpio, sizeof(ctrl->gpio));
while (pulses--)
{
TogglePin(&ctrl->gpio);
DWT_Delay(5); // 5us pulse for 100kHz period
}
ReInit_I2C(port, 1);
ctrl->guard = 0;
}
// -----------------------------------------------------------------------------
//
static uint8_t I2C_Is_Busy(uint8_t port)
{
int bsy,
tp = I2C_SBTimeoutInit(port, 2*BITS_ONE_BYTE); // wait as for two bytes
do bsy = I2C_BUSY(control[port].i2c);
while ((bsy != 0) && !I2C_SBTimeoutExpired(&tp));
return (bsy != 0);
}
// -----------------------------------------------------------------------------
//
// Transaction tail handlers:
//
static void I2C_Success_Handler(uint8_t port)
{
I2C_Control_Type *ctrl = &control[port];
I2C_TypeDef *i2c = ctrl->i2c;
I2C_DISABLE_IE(i2c);
#if (USE_I2C_RX_DMA1)

DMA_STOP(ctrl->rx_DMAChannel);
#endif
#if (USE_I2C_TX_DMA1)

DMA_STOP(ctrl->tx_DMAChannel);
#endif
I2C_ISSUE_STOP(i2c);
ctrl->complete = 1;
I2C_FINISH_CALLBACK(port, ctrl, I2C_RETURN_OK);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// Issues STOP after Error only if the MCU has been a master on the bus.
//
static void I2C_May_STOP(uint8_t port, uint32_t e)
{
I2C_Control_Type *ctrl = &control[port];
I2C_TypeDef *i2c = ctrl->i2c;
if (e & I2C_FLAG_BUSY)
{
I2C_ISSUE_STOP(i2c);
if ((I2C_GET_CR2(i2c) & I2C_START_BIT) ||
!ReadPin(&ctrl->lines[1])) // SDA still LOW?
{
I2C_Recover(port);
}
} else
I2C_GET_DR(i2c);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

static void I2C_Cancel_Handler(uint8_t port)
{
I2C_Control_Type *ctrl = &control[port];
I2C_TypeDef *i2c = ctrl->i2c;

I2C_DISABLE_IE(i2c);
#if (USE_I2C_RX_DMA1)

DMA_STOP(ctrl->rx_DMAChannel);
#endif
#if (USE_I2C_TX_DMA1)

DMA_STOP(ctrl->tx_DMAChannel);
#endif
I2C_May_STOP(port, ctrl->code = I2C_GET_SR1(i2c));

ctrl->complete =
ctrl->errorflag = 0;
I2C_FINISH_CALLBACK(port, ctrl, I2C_RETURN_CANCELLED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

static void I2C_Error_Handler(uint8_t port, uint32_t e)
{
I2C_Control_Type *ctrl = &control[port];

I2C_DISABLE_IE(ctrl->i2c);
#if (USE_I2C_RX_DMA1)

DMA_STOP(ctrl->rx_DMAChannel);
#endif
#if (USE_I2C_TX_DMA1)

DMA_STOP(ctrl->tx_DMAChannel);
#endif
I2C_May_STOP(port, e);

ctrl->errorflag = 1;
I2C_FINISH_CALLBACK(port, ctrl, I2C_RETURN_ERROR);
}
#define I2C_SUCCESS_HANDLER(n) I2C_Success_Handler(n)
#define I2C_CANCEL_HANDLER(n) I2C_Cancel_Handler(n)
#define I2C_ERROR_HANDLER(n, e) I2C_Error_Handler(n, e)

// -----------------------------------------------------------------------------
// EVENT INTERRUPT HANDLER
// -----------------------------------------------------------------------------

#if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1)
static void DMA_Reload(DMA_Channel_TypeDef* dmac,
void *ptr, uint8_t cnt,
uint8_t enable)
{
DMA_STOP(dmac); // neccessary before reload
if (cnt)
DMA_RELOAD(dmac, cnt);
if (ptr)
DMA_MEMPTR(dmac, (uint32_t)ptr);
if (enable)
DMA_START(dmac);
}
#endif
static void i2c_ReloadTransfer(I2C_Control_Type *ctrl, uint8_t writing)
{
I2C_TypeDef *i2c = ctrl->i2c;
int *pcnt;
#if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1)
DMA_Channel_TypeDef *dmac;
#endif
if (writing)
{
#if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1)
dmac = ctrl->tx_DMAChannel;
#endif
pcnt = &ctrl->wcnt;

} else {
#if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1)
dmac = ctrl->rx_DMAChannel;
#endif
pcnt = &ctrl->rcnt;
}
if (*pcnt > UINT8_MAX)
{
#if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1)
DMA_Reload(dmac, &ctrl->ptr[ctrl->indx], UINT8_MAX, ENABLE);
#endif
I2C_NumberOfBytesConfig(i2c, UINT8_MAX); // continue

} else {
I2C_ReloadCmd (i2c, DISABLE);
#if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1)
DMA_Reload(dmac, &ctrl->ptr[ctrl->indx], *pcnt, ENABLE);
#endif
I2C_NumberOfBytesConfig(i2c, *pcnt); // continue
}
}
#define READING 0
#define WRITING !READING

#if !(USE_I2C_TX_DMA1)
static void i2c_SendData(I2C_Control_Type *ctrl)
{
I2C_TypeDef *i2c = ctrl->i2c;
I2C_SendData(i2c, ctrl->ptr[ctrl->indx++]);
ctrl->icnt--;
if (!--ctrl->wcnt) // the last byte is on the way, make ready for TC
I2C_ITConfig(i2c, I2C_IT_TCI, ENABLE);
}
#endif
#if !(USE_I2C_RX_DMA1)
static void i2c_ReceiveData(I2C_Control_Type *ctrl)
{
I2C_TypeDef *i2c = ctrl->i2c;
ctrl->ptr[ctrl->indx++] = I2C_ReceiveData(i2c);
ctrl->icnt--;
if (!--ctrl->rcnt) // the last byte got, make ready for TC coming soon
I2C_ITConfig(i2c, I2C_IT_TCI, ENABLE);
}
#endif
static void I2Cx_IRQHandler(uint8_t port)
{
I2C_Control_Type *ctrl = &control[port];
I2C_TypeDef *i2c = ctrl->i2c;

// NOTE: flags I2C_IT_BERR, I2C_IT_ARLO, I2C_IT_OVR, I2C_IT_ALERT and I2C_IT_PECERR
// belong to the group of error flags triggering the interrupt ERRIE
if (I2C_GetITStatus(i2c, I2C_IT_NACKF
| I2C_IT_BERR
| I2C_IT_ARLO
| I2C_IT_OVR
| I2C_IT_ALERT
| I2C_IT_PECERR
))
{
// STOP must have been issued by HW after an error on bus.
I2C_ERROR_HANDLER(port, I2C_ReadRegister(i2c, I2C_Register_ISR));

} else
if (!ctrl->rcnt) // writing only
{
#if !(USE_I2C_TX_DMA1)
if (I2C_GetITStatus(i2c, I2C_IT_TXI)) // disabled if TX by DMA
{
i2c_SendData(ctrl); // decrements ctrl->wcnt

} else // TC: can be request to stop (SoftEnd_Mode) or to reload (Reload_Mode)
#endif
if (I2C_GetITStatus(i2c, I2C_IT_TCI))
{
if (I2C_GetFlagStatus(i2c, I2C_FLAG_TCR)) // Request to reload
{
#if (USE_I2C_TX_DMA1)
ctrl->indx += UINT8_MAX;
ctrl->wcnt -= UINT8_MAX;
ctrl->icnt -= UINT8_MAX;
#endif
i2c_ReloadTransfer(ctrl, WRITING);

} else { // to STOP as complete
#if (USE_I2C_TX_DMA1)
ctrl->indx += ctrl->wcnt;
ctrl->icnt -= ctrl->wcnt;
ctrl->wcnt = 0;
#endif
I2C_SUCCESS_HANDLER(port);
}
}
} else { // read only or write-restart-read
#if (USE_I2C_RX_DMA1 && USE_I2C_TX_DMA1)
if (I2C_GetITStatus(i2c, I2C_IT_TCI))
{
if (ctrl->wcnt) // writing preamble took place, make reading
{
ctrl->indx += ctrl->wcnt;
ctrl->icnt -= ctrl->wcnt;
ctrl->wcnt = 0;
I2C_MasterRequestConfig(i2c, I2C_Direction_Receiver);
i2c_ReloadTransfer(ctrl, READING);
I2C_ITConfig(i2c, I2C_IT_NACKI, DISABLE);
I2C_GenerateSTART (i2c, ENABLE);

} else { // reading
if (I2C_GetFlagStatus(i2c, I2C_FLAG_TCR)) // to reload as ctrl->rcnt has been >UINT8_MAX
{
// Assumes, the write preamble cannot be longer than 255 bytes
ctrl->rcnt -= UINT8_MAX;
ctrl->icnt -= UINT8_MAX;
ctrl->indx += UINT8_MAX;
i2c_ReloadTransfer(ctrl, READING);

} else {
ctrl->indx += ctrl->rcnt;
ctrl->icnt -= ctrl->rcnt;
ctrl->rcnt = 0;
I2C_SUCCESS_HANDLER(port);
}
}
}
#elif (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1) // no DMA at all
#error "Either both TX and RX DMA or none (interrupt mode) supported so long"
#else
if (I2C_GetITStatus(i2c, I2C_IT_TXI)) // writing preamble
{
i2c_SendData(ctrl); // decrements ctrl->wcnt

} else
if (I2C_GetITStatus(i2c, I2C_IT_RXI)) // reading bytes
{
i2c_ReceiveData(ctrl); // decrements ctrl->rcnt

} else // TC can be request to stop (SoftEnd_Mode),
// to reload (Reload_Mode) or
// to restart for reading after write preamble
if (I2C_GetITStatus(i2c, I2C_IT_TCI))
{
if (I2C_GetFlagStatus(i2c, I2C_FLAG_TCR)) // to reload as ctrl->rcnt has been >UINT8_MAX
{
// Assumes the write preamble cannot be longer than 255 bytes
i2c_ReloadTransfer(ctrl, READING);

} else
if (ctrl->rcnt) // to restart for reading after writing preamble
{
I2C_MasterRequestConfig(i2c, I2C_Direction_Receiver);
i2c_ReloadTransfer(ctrl, READING);
I2C_ITConfig(i2c, I2C_IT_TXI // no more TX
| I2C_IT_NACKI // not needed while reading
| I2C_IT_TCI, DISABLE);
I2C_ITConfig(i2c, I2C_IT_RXI, ENABLE);
I2C_GenerateSTART(i2c, ENABLE); // restart

} else // to stop (reading complete)
I2C_SUCCESS_HANDLER(port);
}
#endif
}
ctrl->guard = 1;
}
void I2C1_IRQHandler(void) {I2Cx_IRQHandler(0);}
void I2C2_IRQHandler(void) {I2Cx_IRQHandler(1);}

// -----------------------------------------------------------------------------
//
// Informs about the current state of the I2C transaction after call to
// "Read_I2C":
//
// 1: I2C transfer completed successfully.
// 0: Transfer in progress...
// -1: an Error encountered, call I2C_Result to know more.
//
int32_t State_I2C(uint8_t port)
{
return (control[port].errorflag ? I2C_RETURN_ERROR : control[port].complete);
}
// -----------------------------------------------------------------------------
//
// Returns the result of the last I2C transaction.
// In fact this is last stored (I2Cx->SR1 + I2Cx->SR2 << 16)
// The call to this function "destroys" the last result.
//
int32_t I2C_Result(uint8_t port)
{
int32_t
r = control[port].code;
control[port].code = I2C_RETURN_NOTHING;
return r;
}
// -----------------------------------------------------------------------------
//
// "The name is the program..."
//
static int32_t I2C_Start_It(uint8_t port)
{
I2C_Control_Type *ctrl = &control[port];
I2C_TypeDef *i2c = ctrl->i2c;
int32_t cnt = ctrl->icnt,
it_Bits = I2C_IT_NACKI | I2C_IT_ERRI;
uint16_t slave = ctrl->ptr[0]; // assume 7-bit mode only

I2C_DISABLE_IE(i2c);
I2C_ClearFlag(i2c, ~(0));

if (!ctrl->rcnt) // write only operation
{
#if (USE_I2C_TX_DMA1)
DMA_Reload(ctrl->tx_DMAChannel, &ctrl->ptr[1], 0, 0);
#endif
if (ctrl->wcnt > UINT8_MAX)
{
I2C_TransferHandling(i2c, slave, UINT8_MAX, I2C_Reload_Mode, I2C_Generate_Start_Write);
#if (USE_I2C_TX_DMA1)
DMA_Reload(ctrl->tx_DMAChannel, NULL, UINT8_MAX, ENABLE);
#else
it_Bits |= I2C_IT_TCI;
#endif

} else {
I2C_TransferHandling(i2c, slave, ctrl->wcnt, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
#if (USE_I2C_TX_DMA1)
DMA_Reload(ctrl->tx_DMAChannel, NULL, ctrl->wcnt, ENABLE);
#endif
}
#if (USE_I2C_TX_DMA1)
it_Bits |= I2C_IT_TCI; // always for DMA transfers
#else
it_Bits |= I2C_IT_TXI;
#endif

} else { // read or write-restart-read

if (!ctrl->wcnt) // read only
{
if (ctrl->rcnt > UINT8_MAX)
{
I2C_TransferHandling(i2c, slave, UINT8_MAX, I2C_Reload_Mode, I2C_Generate_Start_Read);
#if (USE_I2C_RX_DMA1)
DMA_Reload(ctrl->rx_DMAChannel, &ctrl->ptr[1], UINT8_MAX, ENABLE);
#else
it_Bits |= I2C_IT_TCI; // for TCR flag
#endif

} else {
I2C_TransferHandling(i2c, slave, ctrl->rcnt, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
#if (USE_I2C_RX_DMA1)
DMA_Reload(ctrl->rx_DMAChannel, &ctrl->ptr[1], ctrl->rcnt, ENABLE);
#endif
}
#if (USE_I2C_RX_DMA1)
it_Bits |= I2C_IT_TCI; // always for DMA transfers
#else
it_Bits |= I2C_IT_RXI;
#endif
} else { // write-restart-read

I2C_TransferHandling(i2c, slave, ctrl->wcnt, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
#if (USE_I2C_TX_DMA1)
DMA_Reload(ctrl->tx_DMAChannel, &ctrl->ptr[1], ctrl->wcnt, ENABLE);
it_Bits |= I2C_IT_TCI;
#else
it_Bits |= I2C_IT_TXI;
#endif
}
}
I2C_ITConfig(i2c, it_Bits, ENABLE);

ctrl->guard = 1;
return cnt;
}
// -----------------------------------------------------------------------------
//
// Help function to init the 'tp' for I2C_Loop()
//
static int32_t i2c_init_tp(uint8_t port, int32_t cnt)
{
cnt = 2 * (cnt > 0 ? cnt : 2) * BITS_ONE_BYTE;
return I2C_SBTimeoutInit(port, cnt); // 2 * cnt bytes to wait
}
// -----------------------------------------------------------------------------
//
// Internal loop function common for blocking R/W operations
//
// Returns:
// >0: number of bytes transferred
// -1: an error occurred
// -3: if cancelled by a call back
// else passes the result from the caller function through
//
static int32_t I2C_Loop(uint8_t port, int32_t cnt, I2C_CallBackType proc)
{
if (cnt > I2C_RETURN_NOTHING)
{
int32_t cont, tp = i2c_init_tp(port, cnt);
I2C_Control_Type *ctrl = &control[port];

do {
if (ctrl->guard) // initially set to 1 by I2C_Start_It(), thus
{ // the first assignment to tp always takes place.
/*
The .guard flag set by any interrupt occurred
signals some progress in the transaction.
If no flag has been detected after a certain
period of time, an error is assumed.
*/
ctrl->guard = 0;

// Restart the timeout
tp = i2c_init_tp(port, ctrl->icnt);
}
cont = !I2C_SBTimeoutExpired(&tp) || ctrl->errorflag;

} while (!ctrl->complete && !ctrl->errorflag && cont &&
(proc == NULL || (cont = proc(port, ctrl->icnt))));

if (!cont) // cancelled by the callback/timeout
{
I2C_CANCEL_HANDLER(port);
cnt = I2C_RETURN_CANCELLED;

} else {

cnt = (ctrl->errorflag ? I2C_RETURN_ERROR : (cnt - ctrl->icnt));
if (I2C_IS_BUSY(port)) // still?
I2C_Recover(port);
}
}
return cnt;
}
// -----------------------------------------------------------------------------
//
// Starts the writing transaction on the I2C bus. For more, see comments to
// the function "WriteC_I2C" below but the callback here has another effect:
// if provided, it will be called upon the transaction has finished.
// The parameter of the callback will contain the result code I2C_RETURN_*.
//
// NOTE: the callback is invoked out of the interrupt handler! Don't do
// any actions in the callback that could make the system stall.
//
// Returns:
// >0: number of bytes to be transferred (wcnt)
// 0: no transfer will be executed at all
// -2: I2C bus busy, try later.
//
int32_t Write_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, I2C_CallBackType proc)
{
if (!I2C_IS_BUSY(port))
{
if (!wcnt) return I2C_RETURN_NOTHING;
else {
I2C_Control_Type *ctrl = &control[port];
INIT_I2C_FLAGS(ctrl);

ctrl->ptr = buf; // REMEMBER: buf[0] contains the slave address!
ctrl->rcnt = 0; // no reading
ctrl->icnt = // total bytes counter
ctrl->wcnt = wcnt; // data bytes to write (excluding the slave address!)
ctrl->cb = proc;
return I2C_Start_It(port);
}
} // else I2C Bus is BUSY
return I2C_RETURN_BUSY;
}
// -----------------------------------------------------------------------------
//
// Writes "wcnt" bytes to an I2C device WAITING until the transaction is
// complete (blocking write) calling an user provided callback "proc" out
// of the waiting loop. This callback defined as
//
// typedef uint8_t (*I2C_CallBackType)(uin8_t port, int16_t left),
//
// provides in "left" the number of bytes yet to process and should return
// >0 (TRUE) to continue the transaction or 0 (FALSE) to cancel it.
//
// A 7-bit Device ID (I2C address of the device) to provide in the first
// byte of the I/O buffer (buf[0]), the Data start at buf[1].
//
// Returns:
// >0: number of bytes really transferred
// 0: no transfer executed at all
// -1: an error occurred, call I2C_Result to know more.
// -2: I2C bus busy, try later.
// -3: cancelled by a callback
//
int32_t WriteC_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, I2C_CallBackType proc)
{
return I2C_Loop(port, Write_I2C(port, buf, wcnt, NULL), proc);
}
// -----------------------------------------------------------------------------
//
// As "WriteC_I2C" above but w/o callback
//
int32_t WriteW_I2C(uint8_t port, uint8_t *buf, int32_t wcnt)
{
return I2C_Loop(port, Write_I2C(port, buf, wcnt, NULL), NULL);
}
// -----------------------------------------------------------------------------
//
// Starts the reading transaction on the I2C bus. For more, see comments to
// the function "ReadC_I2C" below but the callback here has another effect:
// if provided, it will be called upon the transaction has finished.
// The parameter of the callback will contain the result code I2C_RETURN_*.
//
// NOTE: the callback is invoked out of the interrupt handler! Don't do
// any actions in the callback that could make the system stall.
//
// Returns:
// >0: number of bytes to be transferred (wcnt + rcnt)
// 0: no transfer will be executed at all
// -2: I2C bus busy, try later.
//
int32_t Read_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, int32_t rcnt, I2C_CallBackType proc)
{
if (!I2C_IS_BUSY(port))
{
if (!rcnt) return I2C_RETURN_NOTHING;
else {
I2C_Control_Type *ctrl = &control[port];
INIT_I2C_FLAGS(ctrl);

ctrl->ptr = buf;
ctrl->rcnt = rcnt;
ctrl->wcnt = wcnt;
ctrl->cb = proc;
ctrl->icnt = ctrl->wcnt + ctrl->rcnt; // to include the WRITE operations
return I2C_Start_It(port);
}
} // else I2C Bus is BUSY
return I2C_RETURN_BUSY;
}
// -----------------------------------------------------------------------------
//
// Reads "rcnt" bytes from an I2C device out WAITING until the transaction is
// complete (blocking read) calling an user provided callback "proc" out of
// the WAITING loop. This callback defined as
//
// typedef uint8_t (*I2C_CallBackType)(uin8_t port, int16_t left),
//
// provides in "left" the number of bytes yet to process and should return
// >0 to continue the transaction or 0 to cancel it.
//
// A 7-bit Device ID (I2C address of the device) to provide in the first
// byte of the I/O buffer (buf[0]).
//
// The function implements a START-ADDR-[WRITE-RESTART-ADDR]-READ-STOP
// sequence. The [WRITE-RESTART-ADDR] is inserted only if (wcnt > 0): a
// WRITE preamble of "wcnt" bytes with RESTART issued afterwards precedes
// the READ transaction. This feature is usefull for random read access
// to EEPROMs or other devices with the similar access protocol.
//
// The bytes read are stored back in the buffer starting at buf[wcnt+1],
// while the bytes to write, if any, are provided in buf[1] to buf[wcnt].
//
// FYI: For EEPROMs with >256 bytes capacity the first address byte
// carries MSB and the second - LSB!
//
// Returns:
// >0: number of bytes have been really transferred (wcnt + rcnt mostly)
// 0: no transfer executed at all
// -1: an error occurred, call I2C_Result to know more.
// -2: I2C bus busy, try later.
// -3: cancelled by callback
//
int32_t ReadC_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, int32_t rcnt,
I2C_CallBackType proc)
{
return I2C_Loop(port, Read_I2C(port, buf, wcnt, rcnt, NULL), proc);
}
// -----------------------------------------------------------------------------
//
// As "ReadC_I2C" above but w/o call back.
//
int32_t ReadW_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, int32_t rcnt)
{
return I2C_Loop(port, Read_I2C(port, buf, wcnt, rcnt, NULL), NULL);
}
// -----------------------------------------------------------------------------
//
// Returns TRUE if I2C port 'port' is busy
//
uint8_t Busy_I2C(uint8_t port)
{
return I2C_BUSY(control[port].i2c);
}
// -----------------------------------------------------------------------------
//
// Changes the speed on I2C bus.
//
// Input: a value in Hz
// 100000 would set 100kHz.
//
// ATTENTION: no range check!
//
// Returns:
// >0: OK
// -2: I2C Bus is busy, no changes on-the-fly allowed!
//
int32_t Speed_I2C(uint8_t port, uint32_t rate)
{
if (!I2C_IS_BUSY(port))
{
if (!rate)
rate = I2C_CLOCKSPEED;
control[port].rate = rate;
control[port].icnt = 0;
ReInit_I2C(port, 1);
return control[port].rate/1000;

} else
return I2C_RETURN_BUSY;
}
// -----------------------------------------------------------------------------
//
// Provide:
// the desired I2C bus rate in Hz (below/equal 400000).
// If rate == 0, the value predefined in stm32f0xx_iic.h is used.
//
void Init_I2C(uint8_t port, uint32_t rate)
{
uint8_t cnt = 3;

DWT_Init();
#if (USE_I2C_TX_DMA1 || USE_I2C_RX_DMA1)
RCC_Configuration (DMA1Clocks, sizeof(DMA1Clocks));
#endif
while (Speed_I2C(port, rate) == I2C_RETURN_BUSY && cnt--)
{
I2C_Recover(port);
}
}
//------------------ EOF -------------------------------------------------------

Здесь куча условных трансляций в зависимости от того, хочу я DMA, или только по прерываниям. Используется симуляция DWT счетчика для подсчета коротких задержек на случай stall состояний слейвов, и прочая кухня, которая выросла за многие годы. В общем, без бутылки и меня рядом не разберешься. Поэтому код как таковой вряд ли будет в помощь. Я утверждаю, что задача решаема, т.к. у меня все работает, но поможет ли Вам приведенный код, и не забанит ли меня модерация за "многа буков", я не знаю.

Сообщение отредактировал KnightIgor - Nov 9 2015, 11:31
Go to the top of the page
 
+Quote Post

2 страниц V  < 1 2
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 20th July 2025 - 11:39
Рейтинг@Mail.ru


Страница сгенерированна за 0.01607 секунд с 7
ELECTRONIX ©2004-2016