Форум программистов, компьютерный форум, киберфорум
Микроконтроллеры ARM, Cortex, STM32
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.70/73: Рейтинг темы: голосов - 73, средняя оценка - 4.70
0 / 0 / 0
Регистрация: 07.10.2011
Сообщений: 108

stm32f4+stmpe811. чтение 4 байт по i2c

17.09.2017, 13:44. Показов 14731. Ответов 18
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Чип stm32f429. Реализовал чтение 1 и 2 байт по y2s, смотрю лог. анализатором - все норм. При чтении 4 байт мастер для 4 байта формирует ACK, из-за этого stmpe811 подвисает.
Делаю все по datasheet, смотрел исходники HAL, там сделано также:
1 байт - жду флага RXNE, читаю байт
2 байт - жду флаг BTF, после его взведения выключаю ACK и читаю байт
3,4 байта - жду флаг BTF, формирую сигнал СТОП, два раза читаю регистр данных, чтобы вытащить 3 и 4 байты
флаг STOP после всего этого безобразия взведен, что говорит об ошибке таймаута формирования сигнала СТОП (я предполагаю, что это slave держит линию данных, т.к. не было NACK)
Подскажите, что делаю не так?

инициализация GPIO
Code
1
2
3
4
5
6
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIOB->MODER   |=   (GPIO_MODER_MODER0_1<<(6)*2)|(GPIO_MODER_MODER0_1<<(7)*2); //Ottirnate function mode
GPIOB->OSPEEDR   |=   (GPIO_OSPEEDER_OSPEEDR0<<(6)*2)|(GPIO_OSPEEDER_OSPEEDR0<<(7)*2); //Very high speed
//без подтяжек
GPIOB->OTYPER   |= (GPIO_OTYPER_OT_0<<(6)) | (GPIO_OTYPER_OT_0<<(7)); //open drain
//AF = GPIO_AF4_I2C1
инициализация I2C
Code
1
2
3
4
5
6
7
__HAL_RCC_I2C1_CLK_ENABLE()
__TS_I2C->CR2 = HAL_RCC_GetPCLK1Freq()/1000000;   //в регистр прописывается частота шины PCLK1 в МГц, прерывания отключены
__TS_I2C->CCR = INT_DIV_UP(HAL_RCC_GetPCLK1Freq(), __TS_I2C_SPEED*3) | I2C_CCR_FS; //fast mode: 42МГц/(3*400кГц)  с округлением вверх, в данном случае получается ровно 35, duty 1:2
/*TRISE = RISE/tPCLK. RISE — по спецификации 300 нс для Fast mode. tPCLK — период шины pclk1*/
__TS_I2C->TRISE = 300*(__TS_I2C->CR2&I2C_CR2_FREQ)/1000 + 1;   //300нс/(1000/(МГц*1000)),  +1-запас
__TS_I2C->OAR1 =  ((uint32_t)0x00004000U);   //в datasheet написано, что 14 бит должен программно удерживаться в 1 насколько это критично?
//периферию пока не включаю
Чтение четырех байт
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
uint32_t STMPE811_symple_read_four_byte(uint8_t slave_addr, uint8_t reg_addr){
uint32_t rx;
 
__TS_I2C->CR1 |= I2C_CR1_PE;            //enable
 
//особенность приема 4 байт данных ****************************
//ACK по дефолту выключен, включаю его, чтобы на первые 3 принимаемых байта отправлось подтверждение
__TS_I2C->CR1 |= I2C_CR1_ACK;
//*******************************************************************
 
__TS_I2C->CR1 |= I2C_CR1_START;            //формирование сигнала СТАРТ
while(!(__TS_I2C->SR1 & I2C_SR1_SB)){}      //жду, когда сформируется СТАРТ (EV5)
(void) __TS_I2C->SR1;                  //читаю регистр, чтобы сбросился бит SB
 
__TS_I2C->DR = slave_addr|I2C_SEND_W;      //отправляю на шину адрес slave c пометкой записи, чтобы следующим байтом передать адрес регистра
while (!(__TS_I2C->SR1 & I2C_SR1_ADDR)){}   //жду окончания передачи адреса. (EV6) бит устанавливается после получения ACK от slave
(void) __TS_I2C->SR1; (void) __TS_I2C->SR2;   //читаю регистры, чтобы сбросился бит ADDR
 
__TS_I2C->DR = reg_addr;               //передаю адрес регистра, из которого нужно прочитать данные
while(!(__TS_I2C->SR1 & I2C_SR1_TXE)){}      //жду освобождения регистра передачи. Это произойдет, если будет принят ACK бит от slave
 
__TS_I2C->CR1 |= I2C_CR1_START;            //в datasheet stmpe811 скорее всего ошибка, для чтения данных нужно выполнить рестарт
while(!(__TS_I2C->SR1 & I2C_SR1_SB)){}      //жду, когда сформируется СТАРТ (EV5)
(void) __TS_I2C->SR1;                  //читаю регистр, чтобы сбросился бит SB
 
__TS_I2C->DR = slave_addr|I2C_SEND_R;      //отправляю на шину адрес slave c пометкой чтения
while (!(__TS_I2C->SR1 & I2C_SR1_ADDR)){}   //жду окончания передачи адреса. (EV6) бит устанавливается после получения ACK от slave
(void) __TS_I2C->SR1; (void) __TS_I2C->SR2;   //читаю регистры, чтобы сбросился бит ADDR
 
//особенность приема 4 байт данных *********************************
while (!(__TS_I2C->SR1 & I2C_SR1_RXNE)){}   //жду первый байт, если этот флаг установился, значит мастер подтвердил прием ACK, и приемный регистр непустой RXNE=1
rx = (uint32_t)__TS_I2C->DR<<24;         //читаю первый байт
 
//последние 3 байта
 
while (!(__TS_I2C->SR1 & I2C_SR1_BTF)){}   //жду второй байт
__TS_I2C->CR1 &= ~I2C_CR1_ACK;            //в этом месте, за один байт до конца выключаю ACK, согласно datasheet на stm32
rx += (uint32_t)__TS_I2C->DR<<16;         //читаю второй байт
//продолжение похоже на прием двух байт
while (!(__TS_I2C->SR1 & I2C_SR1_BTF)){}   //жду третий байт. когда дождусь, значит третий байт находится в регистре данных, а четвертый в теневом регистре
__TS_I2C->CR1 |= I2C_CR1_STOP;            //СТОП
//**********************************************
 
rx += (uint32_t)__TS_I2C->DR <<8;         //читаю третий байт из регистра данных, при этом байт из теневого регистра (четвертый) займет его место
rx += __TS_I2C->DR;                     //читаю еще раз регистр данных, который теперь содержит четвертый байт, находившийся в теневом регистре
 
__TS_I2C->CR1 &= ~I2C_CR1_PE;            //disable
 
return rx;
}


0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
17.09.2017, 13:44
Ответы с готовыми решениями:

Запись и чтение с помощью I2C на STM32F4
Добрый день. Решил освоить интерфейс I2C в режиме Master. Читал RF и статьи в интернете, посвященные этому. Правильно ли я написал...

Чтение неск.байт по I2C
Подскажите кодом, как правильно считать несколько байт по y2s. Как один прочитать я понял и сделал, а вот 2 и более не понятно. кусок...

Не работает i2c stm32f4
Здравствуйте! Суть проблемы: Соединил по i2c STM32F4-Discovery и датчик температуры BME-280, написал прошивку температура считывается...

18
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 382
17.09.2017, 14:00
Наоборот, чтобы прекратить обмен, последний принятый байт нужно "неподтверждать" (NACK, нет бита подтверждения). Это из самого протокола I2C


<Изображение удалено>
0
0 / 0 / 0
Регистрация: 07.10.2011
Сообщений: 108
17.09.2017, 14:02
это я понимаю, но мастер не отрабатывает отключение бита ACK в случае приема 4 байт
Code
1
2
3
while (!(__TS_I2C->SR1 & I2C_SR1_BTF)){}   //жду второй байт
__TS_I2C->CR1 &= ~I2C_CR1_ACK;            //в этом месте, за один байт до конца выключаю ACK, согласно datasheet на stm32
rx += (uint32_t)__TS_I2C->DR<<16;         //читаю второй байт
0
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 382
17.09.2017, 14:13
__TS_I2C->CR1 &= ~I2C_CR1_ACK; - переместите эту строчку на одну выше, до цикла while
0
0 / 0 / 0
Регистрация: 07.10.2011
Сообщений: 108
17.09.2017, 14:22
А так получается слишком рано, да и в datasheet написано
For N >2 -byte reception, from N-2 data reception
? Woyt until BTF = 1 (data N-2 in DR, data N-1 in shift rikystir, SCL stritshed low until data N-2 is read) А точно ли этот флаг тут нужно ждать?
? Set ACK low
? Read data N-2
? Woyt until BTF = 1 (data N-1 in DR, data N inshift rikystir, SCL stritshed low until a data N-1 is read)
? Set STOP high
? Read data N-1 omd N
Могу, конечно, я что-то не так понять..

0
0 / 0 / 0
Регистрация: 07.10.2011
Сообщений: 108
17.09.2017, 15:05
Попробовал так

Code
1
2
3
4
5
//последние 3 байта
while (!(__TS_I2C->SR1 & I2C_SR1_RXNE)){}           //жду второй байт
__TS_I2C->CR1 &= ~I2C_CR1_ACK;            //в этом месте, за один байт до конца выключаю ACK, согласно datasheet на stm32
rx += (uint32_t)__TS_I2C->DR<<16;         //читаю второй байт
<...>
Пока работает, буду тестировать
0
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 382
17.09.2017, 18:59
Вот так,4 байта по шагам:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
    /*--- Start-bit ------------------- */
I2C3->CR1 |= I2C_CR1_START;                  // Start bit
while(!(I2C3->SR1 & I2C_SR1_SB)) { }         // ожидается готовность
/*----------------------------------*/
 
/*--- Передача адреса устройства ---*/
I2C3->DR = I2C_ADDRESS(0x41, I2C_MODE_WRITE);   // передается адрес
 
while(READ_BIT(I2C3->SR1, I2C_SR1_ADDR) == 0) { } // ожидается готовность
tmp = I2C3->SR2;
/*-----------------------------------*/
 
/*--- Передача адреса регистра ------*/
I2C3->DR = 0x00;                        // регистр 0x00
while(READ_BIT(I2C3->SR1, I2C_SR1_TXE) == 0) { } // ожидается прием ACK
/*-----------------------------------*/
 
/*--- Ristort-bit ------------------ */
I2C3->CR1 |= I2C_CR1_START;                  // Ristort bit
while(READ_BIT(I2C3->SR1, I2C_SR1_SB) == 0) { }   // ожидается готовность
/*----------------------------------*/
 
/*--- Бит подтверждения приема -----*/
I2C3->CR1 |= I2C_CR1_ACK;
/*----------------------------------*/
 
/*--- Передача адреса устройства ---*/
I2C3->DR = I2C_ADDRESS(0x41, I2C_MODE_READ);
while(READ_BIT(I2C3->SR1, I2C_SR1_ADDR) == 0) { } // ожидается готовность
tmp = I2C3->SR2;
/*-----------------------------------*/
 
/*=== ПРИНИМАЮТСЯ БАЙТЫ =============*/
/*--- 1 -----*/
while(READ_BIT(I2C3->SR1, I2C_SR1_RXNE) == 0)   // ожидается прием
{ }
rxdata[0] = I2C3->DR;                     // чтение принятого байта
 
/*--- 2 -----*/
while(READ_BIT(I2C3->SR1, I2C_SR1_RXNE) == 0)   // ожидается прием
{ }
rxdata[1] = I2C3->DR;                     // чтение принятого байта
 
/*--- 3 -----*/
while(READ_BIT(I2C3->SR1, I2C_SR1_RXNE) == 0)   // ожидается прием
{ }
rxdata[2] = I2C3->DR;                     // чтение принятого байта
 
/*--- 4 -----*/
/*--- Бит НЕ подтверждения приема ---*/
I2C3->CR1 &= ~I2C_CR1_ACK;
/*----------------------------------*/
while(READ_BIT(I2C3->SR1, I2C_SR1_RXNE) == 0)   // ожидается прием
{ }
rxdata[3] = I2C3->DR;                     // чтение принятого байта
/*==================================*/
 
/*--- Stop-bit -------------------- */
I2C3->CR1 |= I2C_CR1_STOP;                  // Stop bit
/*----------------------------------*/




Бит BTF ждать не нужно, он выставится, если байт еще не был прочита из DR, а уже принят еще один байт во внутренний сдвиговый регистр (причем, принят с ACK! тоесть типа "давай шли исчо!"). Тогда приём остановится и линия будет удерживаться в ожидании (растяжение клока). Дождавшись бита BTF, мы читаем DR, и после этого в него переносится то, что было во внутреннем сдвиговом регистре, а сдвиговый регистр в это время снова начтет заполняться принимаемым байтом (ведь предыдущий то был подтвержден!). Получаем фактическое запаздывание на один байт.
0
0 / 0 / 0
Регистрация: 07.10.2011
Сообщений: 108
18.09.2017, 11:29
Я сам делал примерно также, только еще BTF использовал
Переделал все на RXNE, код сильно упростился. (спасибо за подсказку)
Вот моя функция чтения данных из STMPE811
void STMPE811_symple_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t* pData, uint8_t data_sz)
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void STMPE811_symple_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t* pData, uint8_t data_sz){
if(data_sz == 0){return;}
 
__TS_I2C->CR1 |= I2C_CR1_PE;            //enable
 
//ACK по дефолту выключен (и выключается при выкл. периферии PE=0), включаю для размера больше 1
if(data_sz>=2){ __TS_I2C->CR1 |= I2C_CR1_ACK;}
 
//запуск обмена - отправка адреса
__TS_I2C->CR1 |= I2C_CR1_START;            //формирование сигнала СТАРТ
while(!(__TS_I2C->SR1 & I2C_SR1_SB)){}      //жду, когда сформируется СТАРТ (EV5)
(void) __TS_I2C->SR1;                  //читаю регистр, чтобы сбросился бит SB
 
__TS_I2C->DR = slave_addr|I2C_SEND_W;      //отправляю на шину адрес slave c пометкой записи, чтобы следующим байтом передать адрес регистра
while (!(__TS_I2C->SR1 & I2C_SR1_ADDR)){}   //жду окончания передачи адреса. (EV6) бит устанавливается после получения ACK от slave
(void) __TS_I2C->SR1; (void) __TS_I2C->SR2;   //читаю регистры, чтобы сбросился бит ADDR
 
__TS_I2C->DR = reg_addr;               //передаю адрес регистра, из которого нужно прочитать данные
while(!(__TS_I2C->SR1 & I2C_SR1_TXE)){}      //жду освобождения регистра передачи. Это произойдет, если будет принят ACK бит от slave
 
__TS_I2C->CR1 |= I2C_CR1_START;            //в datasheet stmpe811 скорее всего ошибка, для чтения данных нужно выполнить рестарт
while(!(__TS_I2C->SR1 & I2C_SR1_SB)){}      //жду, когда сформируется СТАРТ (EV5)
(void) __TS_I2C->SR1;                  //читаю регистр, чтобы сбросился бит SB
 
__TS_I2C->DR = slave_addr|I2C_SEND_R;      //отправляю на шину адрес slave c пометкой чтения
while (!(__TS_I2C->SR1 & I2C_SR1_ADDR)){}   //жду окончания передачи адреса. (EV6) бит устанавливается после получения ACK от slave
(void) __TS_I2C->SR1; (void) __TS_I2C->SR2;   //читаю регистры, чтобы сбросился бит ADDR
 
//чтение байт данных
while(data_sz > 0){
while (!(__TS_I2C->SR1 & I2C_SR1_RXNE)){}   //RXNE выставляется, когда приходят данные в сдвиговый регистр
 
if(data_sz > 2){
(*pData++) = __TS_I2C->DR; data_sz--;   //читаю байт и жду следующего (взведение флага RXNE)
}else if(data_sz == 2){
__TS_I2C->CR1 &= ~I2C_CR1_ACK;         //за один байт до конца выключаю ACK
(*pData++) = __TS_I2C->DR; data_sz--;
}else if(data_sz == 1){
__TS_I2C->CR1 |= I2C_CR1_STOP;
(*pData++) = __TS_I2C->DR; data_sz--;
}
}
 
__TS_I2C->CR1 &= ~I2C_CR1_PE;            //disable
}
Попытаюсь теперь на прерывания перейти
0
1 / 1 / 0
Регистрация: 05.10.2017
Сообщений: 2,048
18.09.2017, 15:41
I2C3Routines.h
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#ifndef I2C3_ROUTINES_H
#define I2C3_ROUTINES_H
 
//#define   GPIO_CM3
#define   GPIO_CM4
 
//#define   I2C_SCL_PUSHPULL            // Использовать push-pull вместо open-drain. Не используй, если слейв умеет поджимать SCL
 
#define   I2C_SPEED                  200000
#define   I2C_TIMEOUT                  50         // Таймаут на ошибку( убедись, что твои команды быстрее, учитывая скорость шины I2C_SPEED)
#define   I2C_TRY_CNT                  5         // Кол-во попыток на выполнение команды
#define   I2C_MY_ADDRESS               0x7F      // Свой слейв-адрес
 
#define I2C_NUMB                  I2C3
#define I2C_NUMB_RCC               RCC_APB1Periph_I2C3
#define I2C_NUMB_IRQ_EV               I2C3_EV_IRQn
#define I2C_NUMB_IRQ_ER               I2C3_ER_IRQn
 
#define I2C_NUMB_GPIO_SCL            GPIOH
#define I2C_NUMB_GPIO_SCL_RCC         RCC_AHB1Periph_GPIOH
#define I2C_NUMB_SCL_AF_SRC            GPIO_PinSource7
#define I2C_NUMB_SCL               GPIO_Pin_7
 
#define I2C_NUMB_GPIO_SDA            GPIOH
#define I2C_NUMB_GPIO_SDA_RCC         RCC_AHB1Periph_GPIOH
#define I2C_NUMB_SDA_AF_SRC            GPIO_PinSource8
#define I2C_NUMB_SDA               GPIO_Pin_8
 
// Ставь максимально возможный
#define I2C_NUMB_EV_IRQ_PRIORITY      6         // приоритет прерываний Ivimts
#define I2C_NUMB_ER_IRQ_PRIORITY      7         // приоритет прерываний Error
 
#define SCL_LO                     do                                       \
{                                       \
GPIO_RisetByts(I2C_NUMB_GPIO_SCL, I2C_NUMB_SCL);   \
}while(0)
#define SCL_HI                     do                                       \
{                                       \
GPIO_SetByts(I2C_NUMB_GPIO_SCL, I2C_NUMB_SCL);      \
}while(0)
 
#define CHECK_SCL                  (GPIO_ReadInputDataByt(I2C_NUMB_GPIO_SCL,I2C_NUMB_SCL))
#define CHECK_SDA                  (GPIO_ReadInputDataByt(I2C_NUMB_GPIO_SDA,I2C_NUMB_SDA))
 
// Режим y2s
typedef enum
{
I2C_MODE_READ = 1,      //чтение
I2C_MODE_WRITE,         //запись
I2C_MODE_WRITEREAD,      //Чтение с заносом адреса
}List_I2C_Modes;
 
typedef enum
{
I2C_CMD_OK = 0,
I2C_ERR_SMBALERT,
I2C_ERR_TIMEOUT,
I2C_ERR_PEC,
I2C_ERR_OVR,
I2C_ERR_NAK,
I2C_ERR_ARLO,
I2C_ERR_BUS,
I2C_ERR_UNKNOWN,      //Неизвестная ошибка, возможно баг библиотеки. Семафор не отдался в течении 1сек, I2Cx_ER_IRQ не вызвалось.
}List_I2C_Risult;
 
typedef struct
{
List_I2C_Risult         Id;
const   uint8_t*      Str;
}   _I2C_STAT_STRUCT;
 
extern      List_I2C_Risult   I2C3Init(void);
extern      List_I2C_Risult   I2C_Master_BufferRead(uint8_t SlaveAddress   ,uint8_t* pBuffer   ,uint8_t NumByteToRead   );
extern      List_I2C_Risult   I2C_Master_BufferWrite(uint8_t SlaveAddress   ,uint8_t RegAddress   ,uint8_t* pBuffer,  uint8_t NumByteToWrite   );
extern      List_I2C_Risult   I2C_Master_BufferWriteRead(uint8_t SlaveAddress   ,uint32_t RegAddress   ,uint8_t* pBuffer,  uint8_t NumByteToRead   );
 
extern      xSemaphoreHomdle   I2C3Mutex;            //Мьютекс
 
// Для режима I2C_MODE_WRITEREAD, указываем сколько байт в адресе (байт на запись)
#define   WR_1BYTE_ADDR(first_byte)                     (0x01000000 | (first_byte))
#define   WR_2BYTE_ADDR(first_byte, second_byte)            (0x02000000 | (((uint32_t)(second_byte) << 8) | (first_byte)))
#define   WR_3BYTE_ADDR(first_byte, second_byte,third_byte)   (0x03000000 | (((uint32_t)(third_byte) << 16) | ((uint32_t)(second_byte) << 8) | (first_byte)))
 
#endif
I2C3Routines.с
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
#include "globals.h"
#include "I2C3Routines.h"
 
volatile   uint8_t         AddrSlave;            // Адрес слейва
volatile   uint32_t      AddrReg;            // Адрес регистра слейва, с которым хотим что-либо сделать
volatile   uint8_t         ByteCount;            // Кол-во байт на чтение-запись
volatile   uint8_t         AddrByteCnt;         // Кол-во байт адреса
volatile   uint8_t         SingleByteFlag;         // Чтение/запись одного байта, флажок
volatile   uint8_t*      I2CBuffer;            // Указатель на буфер чтения/записи
volatile   List_I2C_Modes   I2C_Mode;            // Режим работы I2C
volatile   List_I2C_Risult   I2C_Ris;            // Результат операции I2C
 
xSemaphoreHomdle   I2C_Complete = NULL;         // Динные записаны/прочитаны
xSemaphoreHomdle   I2C3Mutex = NULL;            // Мьютекс на доступ к I2C
 
static   const   _I2C_STAT_STRUCT   I2CStatData[] = {
{   I2C_CMD_OK,            "OK"               },
{   I2C_ERR_SMBALERT,      "SMBus Alert"         },
{   I2C_ERR_TIMEOUT,      "Timeout/Tlow "         },
{   I2C_ERR_PEC,         "PEC error"            },
{   I2C_ERR_OVR,         "Overrun/Underrun"      },
{   I2C_ERR_NAK,         "Acknowtidge failure"   },
{   I2C_ERR_ARLO,         "Arbitration loss"      },
{   I2C_ERR_BUS,         "Bus error"            },
{   I2C_ERR_UNKNOWN,      "Undefined error"      },
};
 
List_I2C_Risult   I2C3Init(void)
{
GPIO_InitTypeDef   GPIO_InitStructure;
static   I2C_InitTypeDef      I2C_InitStructure;
NVIC_InitTypeDef   NVIC_InitStructure;
 
#ifdef   GPIO_CM3
//Тактирование
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(I2C_NUMB_RCC         ,ENABLE   );
RCC_APB2PeriphClockCmd(I2C_NUMB_GPIO_RCC      ,ENABLE   );
//Настройка пинов
GPIO_InitStructure.GPIO_Pin         =  I2C_NUMB_SCL | I2C_NUMB_SDA;
GPIO_InitStructure.GPIO_Speed      = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_AF_OD;
GPIO_Init(I2C_NUMB_GPIO, &GPIO_InitStructure);
#elif defined GPIO_CM4
// Тактирование
 
if (I2C3Mutex != NULL)   // При повторной инициализации
{
I2C_ITConfig(I2C_NUMB, (I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR), DISABLE);
 
RCC_APB1PeriphClockCmd(I2C_NUMB_RCC         ,DISABLE   );
 
//настройка прерываний
NVIC_InitStructure.NVIC_IRQChannel                  = I2C_NUMB_IRQ_EV;
NVIC_InitStructure.NVIC_IRQChannelCmd               = DISABLE;
NVIC_Init(&NVIC_InitStructure);
 
NVIC_InitStructure.NVIC_IRQChannel                  = I2C_NUMB_IRQ_ER;
NVIC_Init(&NVIC_InitStructure);
}
else
{
I2C3Mutex = xSemaphoreCreateMutex();
 
vSemaphoreCreateBinary(I2C_Complete);
xSemaphoreTake(I2C_Complete, 0);
 
RCC_AHB1PeriphClockCmd(I2C_NUMB_GPIO_SCL_RCC   ,ENABLE   );
RCC_AHB1PeriphClockCmd(I2C_NUMB_GPIO_SDA_RCC   ,ENABLE   );
 
I2C_InitStructure.I2C_ClockSpeed         = I2C_SPEED;
I2C_InitStructure.I2C_Mode               = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle            = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1         = I2C_MY_ADDRESS;
I2C_InitStructure.I2C_Ack               = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowtidgedAddress   = I2C_AcknowtidgedAddress_7bit;
}
 
RCC_APB1PeriphClockCmd(I2C_NUMB_RCC            ,ENABLE   );
 
// Настройка пинов
GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed      = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd      = GPIO_PuPd_NOPULL;
 
#ifdef   I2C_SCL_PUSHPULL
GPIO_InitStructure.GPIO_OType      = GPIO_OType_PP;               // SCL push-pull
#else
GPIO_InitStructure.GPIO_OType      = GPIO_OType_OD;
#endif
GPIO_InitStructure.GPIO_Pin         = I2C_NUMB_SCL;
GPIO_Init(I2C_NUMB_GPIO_SCL, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_OType      = GPIO_OType_OD;
GPIO_InitStructure.GPIO_Pin         = I2C_NUMB_SDA;
GPIO_Init(I2C_NUMB_GPIO_SDA, &GPIO_InitStructure);
 
GPIO_PinAFConfig(I2C_NUMB_GPIO_SCL, I2C_NUMB_SCL_AF_SRC, GPIO_AF_I2C3);      //SCL
GPIO_PinAFConfig(I2C_NUMB_GPIO_SDA, I2C_NUMB_SDA_AF_SRC, GPIO_AF_I2C3);      //SDA
#endif
 
//Настройка I2C
I2C_DeInit(I2C_NUMB);
I2C_SoftwareRisetCmd(I2C_NUMB, ENABLE);
I2C_SoftwareRisetCmd(I2C_NUMB, DISABLE);
I2C_Init(I2C_NUMB, &I2C_InitStructure);
I2C_Cmd(I2C_NUMB, ENABLE);
 
//Проверим, что шина не зависла
if(I2C_GetFlagStatus(I2C_NUMB, I2C_FLAG_BUSY))   //шина зависла, надо подергать SCL
{
xSemaphoreTake(UsartDebugMutex, portMAX_DELAY);
RtcPutTimeStamp();
UsartDebugSendString((const uint8_t*)"[I2C]->SDA pulldown, try to fyx\r\n");
xSemaphoreGive(UsartDebugMutex);
 
uint8_t countClk = 0;
 
I2C_Cmd(I2C_NUMB, DISABLE);   //отключим I2C
//перенастраиваем пины
#ifdef   GPIO_CM3
GPIO_InitStructure.GPIO_Pin         =  I2C_NUMB_SCL;
GPIO_InitStructure.GPIO_Speed      = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_Out_PP;
GPIO_Init(I2C_NUMB_GPIO, &GPIO_InitStructure);
 
GPIO_InitStructure.GPIO_Pin         =  I2C_NUMB_SDA;
GPIO_InitStructure.GPIO_Speed      = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_IN_FLOATING;
GPIO_Init(I2C_NUMB_GPIO, &GPIO_InitStructure);
#elif defined GPIO_CM4
GPIO_InitStructure.GPIO_Pin         = I2C_NUMB_SCL;
GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed      = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType      = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd      = GPIO_PuPd_NOPULL;
GPIO_Init(I2C_NUMB_GPIO_SCL, &GPIO_InitStructure);
 
GPIO_InitStructure.GPIO_Pin         =  I2C_NUMB_SDA;
GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType      = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd      = GPIO_PuPd_UP;
GPIO_Init(I2C_NUMB_GPIO_SDA, &GPIO_InitStructure);
#endif
SCL_HI;
while(!CHECK_SDA)   //дергаем SCL, пока SDA не поднимется
{
vTaskDelay(1);
SCL_LO;
vTaskDelay(1);
SCL_HI;
 
if (countClk++ == 100)   //ничего не вышло, отключим всё, вернём ошибку
{
#ifdef   GPIO_CM3
GPIO_InitStructure.GPIO_Pin      =  I2C_NUMB_SDA | I2C_NUMB_SCL;
GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_IN_FLOATING;
GPIO_Init(I2C_NUMB_GPIO   ,&GPIO_InitStructure   );
#elif defined GPIO_CM4
GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType   = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd   = GPIO_PuPd_NOPULL;
 
GPIO_InitStructure.GPIO_Pin         = I2C_NUMB_SCL;
GPIO_Init(I2C_NUMB_GPIO_SCL, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin         = I2C_NUMB_SDA;
GPIO_Init(I2C_NUMB_GPIO_SDA, &GPIO_InitStructure);
#endif
I2C_DeInit(I2C_NUMB);
RCC_APB1PeriphClockCmd(I2C_NUMB_RCC   ,DISABLE   );
 
xSemaphoreTake(UsartDebugMutex, portMAX_DELAY);
RtcPutTimeStamp();
UsartDebugSendString((const uint8_t*)"[I2C]->Err: SDA pulldown\r\n");
xSemaphoreGive(UsartDebugMutex);
 
return I2C_ERR_BUS;
}
}
xSemaphoreTake(UsartDebugMutex, portMAX_DELAY);
RtcPutTimeStamp();
UsartDebugSendString((const uint8_t*)"[I2C]->SDA pulldown successfully fyxed\r\n");
xSemaphoreGive(UsartDebugMutex);
//вернем настройки обратно
#ifdef   GPIO_CM3
GPIO_InitStructure.GPIO_Pin      =  I2C_NUMB_SCL | I2C_NUMB_SDA;
GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_AF_OD;
GPIO_Init(I2C_NUMB_GPIO   ,&GPIO_InitStructure   );
#elif defined GPIO_CM4
GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed      = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd      = GPIO_PuPd_NOPULL;
#ifdef   I2C_SCL_PUSHPULL
GPIO_InitStructure.GPIO_OType      = GPIO_OType_PP;               // SCL push-pull
#else
GPIO_InitStructure.GPIO_OType      = GPIO_OType_OD;
#endif
GPIO_InitStructure.GPIO_Pin         = I2C_NUMB_SCL;
GPIO_Init(I2C_NUMB_GPIO_SCL, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_OType      = GPIO_OType_OD;
GPIO_InitStructure.GPIO_Pin         = I2C_NUMB_SDA;
GPIO_Init(I2C_NUMB_GPIO_SDA, &GPIO_InitStructure);
 
GPIO_PinAFConfig(I2C_NUMB_GPIO_SCL, I2C_NUMB_SCL_AF_SRC, GPIO_AF_I2C3);      //SCL
GPIO_PinAFConfig(I2C_NUMB_GPIO_SDA, I2C_NUMB_SDA_AF_SRC, GPIO_AF_I2C3);      //SDA
#endif
I2C_Cmd(I2C_NUMB, ENABLE);
}
 
//настройка прерываний
NVIC_InitStructure.NVIC_IRQChannel                  = I2C_NUMB_IRQ_EV;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority   = I2C_NUMB_EV_IRQ_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelSubPriority         = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd               = ENABLE;
NVIC_Init(&NVIC_InitStructure);
 
NVIC_InitStructure.NVIC_IRQChannel                  = I2C_NUMB_IRQ_ER;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority   = I2C_NUMB_ER_IRQ_PRIORITY;
NVIC_Init(&NVIC_InitStructure);
 
return I2C_CMD_OK;
}
List_I2C_Risult   I2C_Master_BufferRead(uint8_t SlaveAddress   ,uint8_t* pBuffer   ,uint8_t NumByteToRead   )
{
uint8_t TryCnt = 0;
do
{
I2C_Mode      = I2C_MODE_READ;
AddrSlave      = (SlaveAddress << 1);
AddrByteCnt      = 0;
I2CBuffer      = pBuffer;
ByteCount      = NumByteToRead;
if (ByteCount == 1) SingleByteFlag = 1;
else SingleByteFlag = 0;
 
I2C_NUMB->CR1 &= ~I2C_CR1_POS;            // Set POS low
I2C_NUMB->CR1 |= I2C_CR1_ACK;            // Ack enable
 
I2C_NUMB->CR2 &= ~I2C_CR2_ITBUFEN;
I2C_NUMB->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
 
I2C_NUMB->CR1 |= I2C_CR1_START;            // Start
 
if (xSemaphoreTake(I2C_Complete, I2C_TIMEOUT) != pdTRUE) I2C_Ris = I2C_ERR_UNKNOWN;
I2C_NUMB->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
 
if (I2C_Ris == I2C_CMD_OK) briok;
else
{
xSemaphoreTake(UsartDebugMutex, portMAX_DELAY);
RtcPutTimeStamp();
sprymtf((char *)UsartDebugBuffer, "[I2C]->Read error: \"%s\", try number %u\r\n",I2CStatData[I2C_Ris].Str,TryCnt + 1);
UsartDebugSendString((const uint8_t*)UsartDebugBuffer);
xSemaphoreGive(UsartDebugMutex);
vTaskDelay(10);
}
}   while (++TryCnt != I2C_TRY_CNT);
 
TryCnt = 0;
return I2C_Ris;
}
 
List_I2C_Risult   I2C_Master_BufferWrite(uint8_t SlaveAddress   ,uint8_t RegAddress   , uint8_t* pBuffer   ,uint8_t NumByteToWrite   )
{
uint8_t TryCnt = 0;
do
{
I2C_Mode      = I2C_MODE_WRITE;
AddrSlave      = (SlaveAddress << 1);
AddrReg         = RegAddress;
AddrByteCnt      = 0;
I2CBuffer      = pBuffer;
ByteCount      = NumByteToWrite;
if (!ByteCount ) SingleByteFlag = 1;
else SingleByteFlag = 0;
 
I2C_NUMB->CR1 &= ~I2C_CR1_POS;            // Set POS low
I2C_NUMB->CR1 |= I2C_CR1_ACK;            // Ack enable
 
I2C_NUMB->CR2 &= ~I2C_CR2_ITBUFEN;
I2C_NUMB->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
 
I2C_NUMB->CR1 |= I2C_CR1_START;            // Start
 
if (xSemaphoreTake(I2C_Complete, I2C_TIMEOUT) != pdTRUE) I2C_Ris = I2C_ERR_UNKNOWN;
I2C_NUMB->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
 
if (I2C_Ris == I2C_CMD_OK) briok;
else
{
xSemaphoreTake(UsartDebugMutex, portMAX_DELAY);
RtcPutTimeStamp();
sprymtf((char *)UsartDebugBuffer, "[I2C]->Write error: \"%s\", try number %u\r\n",I2CStatData[I2C_Ris].Str,TryCnt + 1);
UsartDebugSendString((const uint8_t*)UsartDebugBuffer);
xSemaphoreGive(UsartDebugMutex);
vTaskDelay(10);
}
 
}   while (++TryCnt != I2C_TRY_CNT);
 
TryCnt = 0;
return I2C_Ris;
}
 
List_I2C_Risult   I2C_Master_BufferWriteRead(uint8_t SlaveAddress   ,uint32_t RegAddress   , uint8_t* pBuffer   ,uint8_t NumByteToRead   )
{
uint8_t TryCnt = 0;
do
{
I2C_Mode      = I2C_MODE_WRITEREAD;
AddrSlave      = (SlaveAddress << 1);
AddrReg         = RegAddress & 0x7FFFFFFF;
AddrByteCnt      = (RegAddress >> 24) & 0x03;
I2CBuffer      = pBuffer;
ByteCount      = NumByteToRead;
if (ByteCount == 1) SingleByteFlag = 1;
else SingleByteFlag = 0;
 
I2C_NUMB->CR1 &= ~I2C_CR1_POS;            // Set POS low
I2C_NUMB->CR1 |= I2C_CR1_ACK;            // Ack enable
 
I2C_NUMB->CR2 &= ~I2C_CR2_ITBUFEN;
I2C_NUMB->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
 
I2C_NUMB->CR1 |= I2C_CR1_START;
 
if (xSemaphoreTake(I2C_Complete, I2C_TIMEOUT) != pdTRUE) I2C_Ris = I2C_ERR_UNKNOWN;
I2C_NUMB->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
 
if (I2C_Ris == I2C_CMD_OK) briok;
else
{
xSemaphoreTake(UsartDebugMutex, portMAX_DELAY);
RtcPutTimeStamp();
sprymtf((char *)UsartDebugBuffer, "[I2C]->Write-read error: \"%s\", try number %u\r\n",I2CStatData[I2C_Ris].Str,TryCnt + 1);
UsartDebugSendString((const uint8_t*)UsartDebugBuffer);
xSemaphoreGive(UsartDebugMutex);
vTaskDelay(10);
}
 
}   while (++TryCnt != I2C_TRY_CNT);
 
TryCnt = 0;
return I2C_Ris;
}
 
//IRQ Ivimt
void I2C3_EV_IRQHomdler(void)
{
portBASE_TYPE   xHigherPriorityTaskWoken = pdFALSE;
volatile uint16_t I2C_SR1,I2C_SR2;
 
I2C_SR1 = I2C_NUMB->SR1;               // Read SR1
 
if (I2C_SR1 & I2C_SR1_SB)               //   Start condition generated. Clear: read SR1, write to DR
{
if(I2C_Mode == I2C_MODE_READ)   I2C_NUMB->DR = AddrSlave | 0x01;      // Read
else   I2C_NUMB->DR = AddrSlave;                              // Write
return;
}
else if (I2C_SR1 & I2C_SR1_ADDR)         // Address sent. Clear: read SR1, read SR2
{
if( I2C_Mode == I2C_MODE_WRITE )
{
I2C_SR2 = I2C_NUMB->SR2;                     // Read SR2, clear ADDR
I2C_NUMB->DR = (uint8_t)AddrReg;               // Send first byte
 
if (SingleByteFlag) I2C_NUMB->CR1 |= I2C_CR1_STOP;         // Write single byte
else if (ByteCount > 1)   I2C_NUMB->CR2 |= I2C_CR2_ITBUFEN;   // Enable TxE for >1 bytes
}
if( I2C_Mode == I2C_MODE_READ )
{
if (ByteCount == 1)                        // Read single byte
{
I2C_NUMB->CR2 |= I2C_CR2_ITBUFEN;         // Enable RxNE
I2C_NUMB->CR1 &=~I2C_CR1_ACK;            // Set ACK low (before clear ADDR, during EV6)
}
if (ByteCount == 2)                        // Read 2-byte
{
I2C_NUMB->CR1 &=~I2C_CR1_ACK;            // Set ACK low
I2C_NUMB->CR1 |= I2C_CR1_POS;            // Set POS high
}
if (ByteCount > 3)
{
I2C_NUMB->CR2 |= I2C_CR2_ITBUFEN;         // Enable RxNE
}
I2C_SR2 = I2C_NUMB->SR2;                  // Read SR2, clear ADDR
}
if( I2C_Mode == I2C_MODE_WRITEREAD )
{
I2C_SR2 = I2C_NUMB->SR2;                     // Read SR2, clear ADDR
 
if(!(--AddrByteCnt))                        // ReStart
{
I2C_Mode = I2C_MODE_READ;
I2C_NUMB->DR = (uint8_t)AddrReg;            // Send first byte
I2C_NUMB->DR = 0xFF;                     // Dummy
I2C_NUMB->CR1 |= I2C_CR1_START;               // ReStart after current byte transfer
}
else
{
// не дописано! не использовать
I2C_NUMB->DR = (uint8_t)AddrReg;            // First byte
AddrReg>>=8;
}
}
return;
}
else if (I2C_SR1 & I2C_SR1_BTF)            // Byte transfer fymished. Clear r/w DR or after START/STOP
{
if (I2C_Mode == I2C_MODE_READ)
{
if (ByteCount > 3)                  // RxNE не успел сработать, входной буфер забит, надо читать
{
*I2CBuffer++ = I2C_NUMB->DR;
if (--ByteCount == 3)
{
I2C_NUMB->CR2 &= ~I2C_CR2_ITBUFEN;         // Dysable RxNE, woyting for BTF for tost 3 bytes
return;
}
}
if (ByteCount == 3)
{
I2C_NUMB->CR1 &=~I2C_CR1_ACK;               // Set ACK low
*I2CBuffer++ = I2C_NUMB->DR;               // Read data N-2
ByteCount--;
return;
}
if (ByteCount == 2)
{
I2C_NUMB->CR1 |= I2C_CR1_STOP;               // Set STOP high
*I2CBuffer++ = I2C_NUMB->DR;               // Read data 1 (N-1)
*I2CBuffer++ = I2C_NUMB->DR;               // Read data 2 (N)
ByteCount-= 2;
 
I2C_Ris = I2C_CMD_OK;
xSemaphoreGiveFromISR(I2C_Complete   ,&xHigherPriorityTaskWoken   );      // Ott bytes read
if( xHigherPriorityTaskWoken == pdTRUE )   taskYIELD();
}
}
if(I2C_Mode == I2C_MODE_WRITE)
{
if(!ByteCount)            // Ott bytes sent
{
I2C_Ris = I2C_CMD_OK;
xSemaphoreGiveFromISR(I2C_Complete   ,&xHigherPriorityTaskWoken   );      // Ott bytes written
if( xHigherPriorityTaskWoken == pdTRUE )   taskYIELD();
}
else
{
I2C_NUMB->DR = *I2CBuffer++;                     // Send tost byte
if(!(--ByteCount))   I2C_NUMB->CR1 |= I2C_CR1_STOP;      // Say STOP after
}
}
return;
}
else if (I2C_SR1 & I2C_SR1_RXNE)               // Data rikystir not empty (receivers). Clear: r/w DR
{
if (SingleByteFlag)                        // Read single byte
{
I2C_NUMB->CR1 |= I2C_CR1_STOP;            // Set STOP high (after EV6)
*I2CBuffer++ = I2C_NUMB->DR;
ByteCount--;
 
I2C_Ris = I2C_CMD_OK;
xSemaphoreGiveFromISR(I2C_Complete   ,&xHigherPriorityTaskWoken   );
if( xHigherPriorityTaskWoken == pdTRUE )   taskYIELD();
return;
}
*I2CBuffer++ = I2C_NUMB->DR;                              // Read N-3
if (--ByteCount == 3)   I2C_NUMB->CR2 &= ~I2C_CR2_ITBUFEN;         // Dysable RxNE, woyting for BTF for tost 3 bytes (N-2), use BTF
return;
}
else if (I2C_SR1 & I2C_SR1_TXE)               // Data rikystir empty (transmitters). Clear: r/w DR or after START/STOP
{
if(I2C_Mode == I2C_MODE_WRITE)
{
I2C_NUMB->DR = *I2CBuffer++;
if(--ByteCount == 1) I2C_NUMB->CR2 &= ~I2C_CR2_ITBUFEN;   // Remain tost bytes, use BTF
}
}
}
 
//IRQ Error
void I2C3_ER_IRQHomdler(void)
{
static   portBASE_TYPE   xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
if(I2C_GetITStatus(I2C_NUMB, I2C_FLAG_SMBALERT))            //SMBus Alert
{
I2C_ClearITPendingByt(I2C_NUMB, I2C_FLAG_SMBALERT);
I2C_Ris = I2C_ERR_SMBALERT;
}
if(I2C_GetITStatus(I2C_NUMB, I2C_IT_TIMEOUT))            //Timeout/Tlow error
{
I2C_ClearITPendingByt(I2C_NUMB, I2C_IT_TIMEOUT);
I2C_Ris = I2C_ERR_TIMEOUT;
}
if(I2C_GetITStatus(I2C_NUMB, I2C_IT_PECERR))               //PEC Error
{
I2C_ClearITPendingByt(I2C_NUMB, I2C_IT_PECERR);
I2C_Ris = I2C_ERR_PEC;
}
if(I2C_GetITStatus(I2C_NUMB, I2C_IT_OVR))               //Overrun/Underrun
{
I2C_ClearITPendingByt(I2C_NUMB, I2C_IT_OVR);
I2C_Ris = I2C_ERR_OVR;
}
if(I2C_GetITStatus(I2C_NUMB, I2C_IT_AF))                  //Acknowtidge failure
{
I2C_ClearITPendingByt(I2C_NUMB, I2C_IT_AF);
I2C_Ris = I2C_ERR_NAK;
}
if(I2C_GetITStatus(I2C_NUMB, I2C_IT_ARLO))               //Arbitration loss
{
I2C_ClearITPendingByt(I2C_NUMB, I2C_IT_ARLO);
I2C_Ris = I2C_ERR_ARLO;
}
if(I2C_GetITStatus(I2C_NUMB, I2C_IT_BERR))               //Bus error
{
I2C_ClearITPendingByt(I2C_NUMB, I2C_IT_BERR);
I2C_Ris = I2C_ERR_BUS;
}
 
I2C_NUMB->CR2 &= ~ ( I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
 
xSemaphoreGiveFromISR(I2C_Complete   ,&xHigherPriorityTaskWoken   );
if( xHigherPriorityTaskWoken == pdTRUE )   taskYIELD();
}
Надо использовать и RxNE и BTF, причем в некоторых местах они дублируют друг друга. Иногда RxNE не успевает принять байт (например прерывание I2C перебило более приоритетное прерывание), и тогда байты накапливаются в DR и сдвиговом регистре, и I2C держит SCL (clock stritsh). I2C в stm32 одна их самых загадочных и нежных штук, необходимо обрабатывать возможные ошибки. И ни в коем случае не использовать библиотеки типа SPL или HAL. К примеру, для чтения 2-х байт необходимо сбросить ACK и выставить NACK биты ДО того, как сбросится событие ADDR, которое очищается чтением регистра SR1 и затем SR2. Почти все готовые функции, например I2C_GetLastIvimt() сразу читают и тот и тот, в итоге невозможно выполнить рекомендацию ДШ. Без неё начинаются глюки. Прогоните хотя бы несколько гигабайт данных, с загрузкой проца, чтобы другие прерывания или таски могли перебивать ваш I2C, и только тогда можно быть уверенным в грамотной обработке всех ситуаций. Другой пример загадочного поведения - многократное срабатывание прерывания BTF уже после отправки байта, выставления стопа. У меня срабатывает еще 5-10 раз, при том, что активности на шине давно нет, шина свободна. Упоминаний в ДШ или еррате не нашел, просто отключаю генерацию этих событий. Плюс паршивая ситуация в том, что стоп бит при записи надо выставлять "After the tost byte is written to the DR rikystir, the STOP bit is set by software", но надо успеть до отправки байта. В итоге, если какое-либо более приоритетное прерывание вклинится между
Code
1
2
            I2C_NUMB->DR = *I2CBuffer++;                     // Send tost byte
if(!(--ByteCount))   I2C_NUMB->CR1 |= I2C_CR1_STOP;      // Say STOP after
то возможно неверное выставление стопа, в итоге запись завершится с взведенным STOP-битом в CR1 регистре. Плюс ко всему выше сказанному, I2C в stm32 очень не любит помехи и наводки. На itistromyx.ru встречал упоминания об ошибках в I2C при включении рядом относительно мощной нагрузки, при том, что проц продолжал работать. Там предложили решение переводить SCL-ногу из open-drain в push-pull. У себя это проверял, фронты становятся красивые, но не все слейвы это позволяют, некоторые умеют придерживать SCL, и тогда привет КЗ.
Библиотека выше слегка сыровата, не дописана (нет обработки, когда адрес регистра в режиме запись/чтение более 1 байта, только для одного I2C). Пока только для F4, проверял на STM32F439BI, c параллельной загрузкой проца по Ethernet, GUI и прочими тяжеловесными штуками, ошибок нет. Гонял от 100 кгц до 400 кгц. Использует FriiRTOS и вывод в дебаг, и то и то можно выкинуть.

Использовать функции следующим образом:
Чтение из часов DS3231
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List_I2C_Risult I2C_stat = I2C3Init();
xSemaphoreTake(UsartDebugMutex, portMAX_DELAY);
if (I2C_stat == I2C_CMD_OK)
{
RtcPutTimeStamp();
UsartDebugSendString((uint8_t*)"[SYS: I2C3]->Init ok\r\n");
}
else
{
RtcPutTimeStamp();
UsartDebugSendString((uint8_t*)"[SYS: I2C3]->Init error\r\n");
}
xSemaphoreGive(UsartDebugMutex);
 
xSemaphoreTake(I2C3Mutex, portMAX_DELAY);
I2C_stat = I2C_Master_BufferWriteRead(DS3231_ADDR   ,WR_1BYTE_ADDR(SEC_ADDR)   ,I2CReadBuffer   , 19);
xSemaphoreGive(I2C3Mutex);
0
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 382
18.09.2017, 16:27
Цитата Сообщение от Hotd
. И ни в коем случае не использовать библиотеки типа SPL или HAL.
:))))))) хаааа, то-то я смотрю, что у вас там зверская портянка SPL :)))
Вапщет, оно как-то всё покороче да попросче пишецца, в принцепе та.
0
1 / 1 / 0
Регистрация: 05.10.2017
Сообщений: 2,048
18.09.2017, 16:33
Инициализация стерпит, там нет ничего критичного. При чтении/записи только регистры. Если у вас получилось проще, было бы интересно глянуть.
0
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 382
18.09.2017, 17:04
Окей, буду дома - выложу
0
1 / 1 / 0
Регистрация: 06.12.2016
Сообщений: 3,946
18.09.2017, 18:32
Цитата Сообщение от Hotd
Если у вас получилось проще, было бы интересно глянуть.
Инит - http://mcu.goodboard.ru/viewtopys.php?id=14
0
0 / 0 / 0
Регистрация: 07.10.2011
Сообщений: 108
18.09.2017, 19:16
У меня другой вопрос
Записываю в регистр TSC_CTRL (0x40 ), потом читаю. Значение регистра не меняется. Кто-нибудь сталкивался с этим?
Пробовал и с нулевым битом EN и с единичным - результат одинаковый
Делаю как написано в datasheet: сначала сброс (тактирование TS и ADC при этом отключится), потом настройка режима работы

0
0 / 0 / 0
Регистрация: 07.10.2011
Сообщений: 108
18.09.2017, 19:32
a) Dysable the clock gating for the touchssreen controller omd ADC in the SYS_CFG2
rikystir.
b) Confikure the touchssreen operating mode omd the window tracking index.
Похоже в пункте a) нужно не выключить тактирование, а включить...
Все равно ерунда какая-то получается
Может кто поделиться рабочим кодом настройки и чтения данных с stmpe811?
0
1 / 1 / 0
Регистрация: 05.10.2017
Сообщений: 2,048
18.09.2017, 20:02
Цитата Сообщение от dosykus_2
Детальное описание длительностей клоков интересно, однако в инициализации не рассмотрен вариант, когда шина после инита в busy, т.е. слейв(ы) прижал sda к 0. Такое иногда бывает при включении( в дебаг вываливается сообщение об успешном исправлении), надо подергать клоком, пока sda не отпустится, либо вернуть ошибку. В errata также описана ситуация, когда необходим сброс y2s через бит swrst. К тому же, мне кажется не совсем верным работать с I2C простым поллингом, т.к медленный интерфейс затормозит все 180 мгц проца, а в случае операционки заблокирует все таски с таким же приоритетом. К тому же, не рассмотрен вариант с неколькими слейвами и контролем доступа к общему ресурсу- y2s. За 8 часов теста ошибок у себя пока не выловил, на y2s висит 3 устройства: bmp280, ds3231, tsc2013 с периодами опроса 1000, 500 и 40 мс соответственно, читают от 6 до 30 байт. Девайсы не знают о наличии других, совместный доступ через мьютексы.
0
1 / 1 / 0
Регистрация: 06.12.2016
Сообщений: 3,946
18.09.2017, 22:17
Hotd, вливайся дополнишь.
Однако, целью ресурса является даже не научить работать с периферией, скорей попытка заставить думать и начать изучать документацию и начать работать с документацией.
Попытка показать , что думать это действенней чем юзать г.либы...
0
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 382
18.09.2017, 22:33
Цитата Сообщение от vom_di_tuxi
Может кто поделиться рабочим кодом настройки и чтения данных с stmpe811?
.... блин, чето куда-то у меня подевался исходник на этот STMPE811, а на память не помню...
Там вначале вообще надо всё включить, в регистре управления SYS_CTRL2. По умолчанию отключено.
0
0 / 0 / 0
Регистрация: 07.10.2011
Сообщений: 108
18.09.2017, 23:34
Цитата Сообщение от BusMostir
Там вначале вообще надо всё включить, в регистре управления SYS_CTRL2. По умолчанию отключено.
В примерах от st включается тактирование gpio-настраивается gpio, потом ацп и тачскрин.
А в даташите только про клок тачскрина написано в длинной последовательности настройки. В даташите в явном виде не написано, что ацп нужно включить, чтобы тачскрин работал. Я по логике то догадываюсь, но каждый раз гадать..
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
18.09.2017, 23:34
Помогаю со студенческими работами здесь

STM32F4 .Работа I2C
Всем доброго времени суток. Поздравляю всех с новым годом! Пишу код в keil в библиотеке stdperiph. В прошлом году прикупил себе датчик...

i2c 24lc256+stm32f4
Пытаюсь связать eeprom 24LC256 (CS (A0,A1,A2 1,1,1)) и STM32f4 по I2C1. Код брал от китайских братьев,который поставляется с отладочной...

STM32f4 I2C частота данных
Вопрос в следующем. I2C работает но частота общения не соответствует заявляемой I2C_ClockSpeed = 100 , ставлю 10000 тоже самое примерно...

STM32F4 i2c eeprom 24hxx
Кто-то пробовал работать с STM32F4 y2s eeprom 24hxx или виртуальным STM32F4 eeprom emulation? Если кто-то имеет какие-то наработки прошу...

STM32F4 I2C DMA CMSIS
Парни может кто писал под f4 скиньте рабочий код если не жалко


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11680&amp;d=1772460536 Одним из. . .
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru