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

 
 
> STM32F030 проблема с I2C, Непонятное поведение I2C периферии контроллера
p_kav
сообщение Nov 7 2015, 23:52
Сообщение #1


Местный
***

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



Здравствуйте.

Использую контроллер STM32F030F4P6. К нему по I2C подключен 1-Wire Master DS2482-100, а к нему, в свою очередь, по 1-Wire подключен термометр DS1820.

В Keil 5 написан следующий код.

Инициализация I2C:

CODE
void i2c1_init(void)
{
GPIO_InitTypeDef PORT;
I2C_InitTypeDef I2C_INIT;

I2C_StructInit(&I2C_INIT);

I2C_INIT.I2C_Ack = I2C_Ack_Disable;
I2C_INIT.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_INIT.I2C_Mode = I2C_Mode_I2C;
I2C_INIT.I2C_OwnAddress1 = 0xAA;
I2C_INIT.I2C_AnalogFilter = I2C_AnalogFilter_Disable;
I2C_INIT.I2C_DigitalFilter = 0;
I2C_INIT.I2C_Timing = 0x20000A0D; // Расчитано в Excel-таблице I2C_Timing_Configuration_V1.0.1.xls от ST

I2C_DeInit(I2C1);

PORT.GPIO_Mode = GPIO_Mode_AF;
PORT.GPIO_OType = GPIO_OType_OD;
PORT.GPIO_PuPd = GPIO_PuPd_NOPULL;
PORT.GPIO_Speed = GPIO_Speed_2MHz;
PORT.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;

GPIO_Init(GPIOA, &PORT);

GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_4);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_4);

I2C_Init(I2C1, &I2C_INIT);
I2C_Cmd(I2C1, ENABLE);

}


Работа с DS2482-100:

CODE
t1 = 0;
t2 = 0;
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);
I2C_SendData(I2C1, 0xF0);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);
I2C_SendData(I2C1, 0xB4);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

do
{
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
} while (I2C_ReceiveData(I2C1) & 0x01);

I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); // Зависает здесь
I2C_SendData(I2C1, 0xA5);
I2C_SendData(I2C1, 0xCC);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

do
{
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
} while (I2C_ReceiveData(I2C1) & 0x01);

/*
I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);
I2C_SendData(I2C1, 0xA5);
I2C_SendData(I2C1, 0x44);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
while(I2C_ReceiveData(I2C1) & 0x01) {}
*/

I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);
I2C_SendData(I2C1, 0xA5);
I2C_SendData(I2C1, 0xBE);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

do
{
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
} while (I2C_ReceiveData(I2C1) & 0x01);

I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);
I2C_SendData(I2C1, 0x96);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);
I2C_SendData(I2C1, 0xE1);
I2C_SendData(I2C1, 0xE1);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
t1 = I2C_ReceiveData(I2C1);

I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);
I2C_SendData(I2C1, 0x96);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

do
{
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
} while (I2C_ReceiveData(I2C1) & 0x01);

I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);
I2C_SendData(I2C1, 0xE1);
I2C_SendData(I2C1, 0xE1);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);

I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Read);
t2 = I2C_ReceiveData(I2C1);

while(I2C_GetFlagStatus(I2C1, I2C_ISR_STOPF) == RESET);
I2C_ClearFlag(I2C1, I2C_ICR_STOPCF);


Если данный код вручную прошагать в отладчике, то никаких проблем не проявляется и в переменных t1 и t2 будут 0x05 и 0x50, как и указано в даташите на DS18B20.
Однако при обычном запуске программа зависает на четвертом блоке кода на проверке флага TXIS (отмечено комментарием). Флаг TXIS действительно не установлен, при этом флаг TXE установлен, хотя как я понял из даташита, в данном случае они должны подниматься вместе. Также установлен флаг TC, а NACKF, ARLO и другие сняты.
Если установить Breakpoint на строчку выше (I2C_TransferHandling) и выполнить Step Over, то TXIS будет установлен и работа продолжится.

Перепробовал множество вариантов, итог всегда один - пошагово работает, при обычном запуске виснет на проверке флага. У кого-нибудь было такое поведение I2C периферии STM32F0?

Сообщение отредактировал IgorKossak - Nov 8 2015, 17:52
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
p_kav
сообщение Nov 9 2015, 10:51
Сообщение #2


Местный
***

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



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


Знающий
****

Группа: Участник
Сообщений: 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

Сообщений в этой теме
- p_kav   STM32F030 проблема с I2C   Nov 7 2015, 23:52
- - etoja   Цитата(p_kav @ Nov 8 2015, 03:52) Использ...   Nov 8 2015, 17:16
- - p_kav   Цитата(etoja @ Nov 8 2015, 22:16) I2C и 1...   Nov 8 2015, 17:36
|- - Alechek   Цитата(p_kav @ Nov 8 2015, 22:36) Всё вер...   Nov 9 2015, 06:45
|- - scifi   Цитата(Alechek @ Nov 9 2015, 09:45) Так, ...   Nov 9 2015, 07:08
|- - adnega   Цитата(scifi @ Nov 9 2015, 10:08) +1. Кст...   Nov 9 2015, 08:04
- - p_kav   Alechek Что-то не могу найти ни одной статьи о так...   Nov 9 2015, 07:04
|- - Alechek   Цитата(p_kav @ Nov 9 2015, 12:04) Что-то ...   Nov 9 2015, 08:05
- - adnega   По теме: флаги под отладчиком могут меняться, если...   Nov 9 2015, 08:07
|- - scifi   Цитата(adnega @ Nov 9 2015, 11:07) По тем...   Nov 9 2015, 08:15
- - p_kav   scifi Я пробовал вставлять задержки, не помогает. ...   Nov 9 2015, 08:28
|- - scifi   Цитата(p_kav @ Nov 9 2015, 11:28) мне каж...   Nov 9 2015, 08:41
|- - AVI-crak   Цитата(p_kav @ Nov 9 2015, 15:28) К тому ...   Nov 9 2015, 09:03
- - p_kav   Цитата(scifi @ Nov 9 2015, 13:41) А ещё п...   Nov 9 2015, 08:47
|- - KnightIgor   Цитата(p_kav @ Nov 9 2015, 09:47) Вот, на...   Nov 9 2015, 10:21
- - vet   Пытался работать с I2C-периферией и на STM32F, и н...   Nov 9 2015, 09:10
- - p_kav   Цитата(AVI-crak @ Nov 9 2015, 14:03)...   Nov 9 2015, 09:13
|- - adnega   Цитата(p_kav @ Nov 9 2015, 12:13) остаетс...   Nov 9 2015, 09:22
- - p_kav   Цитата(KnightIgor @ Nov 9 2015, 15:21) ТС...   Nov 9 2015, 10:33
|- - KnightIgor   Цитата(p_kav @ Nov 9 2015, 11:33) А код о...   Nov 9 2015, 10:50


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

 


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


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