stm32f1 cmsis настройка I2C

Кто любит RISC в жизни, заходим, не стесняемся.
Аватара пользователя
Мурик
Друг Кота
Сообщения: 3383
Зарегистрирован: Пн окт 11, 2010 19:00:08

Re: stm32f1 cmsis настройка I2C

Сообщение Мурик »

А что вас интересует? Полностью готовое решение, написанное за вас кем-то другим?
Реклама
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

[uquote="Мурик",url="/forum/viewtopic.php?p=3878397#p3878397"]У F103 есть только одна существенная проблема с I2C.
I2C analog filter may provide wrong value, locking BUSY flag and preventing master mode entry
Description
The I2C analog filters embedded in the I2C I/Os may be tied to low level, whereas SCL and
SDA lines are kept at high level. This can occur after an MCU power-on reset, or during
ESD stress. Consequently, the I2C BUSY flag is set, and the I2C cannot enter master mode
(START condition cannot be sent). The I2C BUSY flag cannot be cleared by the SWRST
control bit, nor by a peripheral or a system reset. BUSY bit is cleared under reset, but it is
set high again as soon as the reset is released, because the analog filter output is still at low
level. This issue occurs randomly.
Note: Under the same conditions, the I2C analog filters may also provide a high level, whereas
SCL and SDA lines are kept to low level. This should not create issues as the filters output
will be correct after next SCL and SDA transition.
Возникает при сильных помехах на I2C (кто-то туда полез тыкать отверткой или пинцетом и куда попало коротить). Решается программно.
Workaround
The SCL and SDA analog filter output is updated after a transition occurs on the SCL and
SDA line respectively. The SCL and SDA transition can be forced by software configuring
the I2C I/Os in output mode. Then, once the analog filters are unlocked and output the SCL
and SDA lines level, the BUSY flag can be reset with a software reset, and the I2C can enter
master mode. Therefore, the following sequence must be applied:
1. Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register.
2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level
(Write 1 to GPIOx_ODR).
3. Check SCL and SDA High level in GPIOx_IDR.
4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
5. Check SDA Low level in GPIOx_IDR.
6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
7. Check SCL Low level in GPIOx_IDR.
8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to
GPIOx_ODR).
9. Check SCL High level in GPIOx_IDR.
10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to
GPIOx_ODR).
11. Check SDA High level in GPIOx_IDR.
12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
13. Set SWRST bit in I2Cx_CR1 register.
14. Clear SWRST bit in I2Cx_CR1 register.
15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register.
Я не считаю что из-за этого стоит отказываться от МК.[/uquote]

Добавлено after 1 minute 8 seconds:
Каким образом это реализовывать?.В инециализации?
Реклама
a797945
Мучитель микросхем
Сообщения: 446
Зарегистрирован: Вс ноя 01, 2015 09:15:16
Откуда: 69.Ржев

Re: stm32f1 cmsis настройка I2C

Сообщение a797945 »

перепишите эту процедуру в понятных Вам терминах и Вы поймете что к параметрам инита это никакого отношения не имеет.
протокол проверки линий перед тем как вновь поднять интерфейс.
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

[uquote="Мурик",url="/forum/viewtopic.php?p=3878397#p3878397"]У F103 есть только одна существенная проблема с I2C.
I2C analog filter may provide wrong value, locking BUSY flag and preventing master mode entry
Description
The I2C analog filters embedded in the I2C I/Os may be tied to low level, whereas SCL and
SDA lines are kept at high level. This can occur after an MCU power-on reset, or during
ESD stress. Consequently, the I2C BUSY flag is set, and the I2C cannot enter master mode
(START condition cannot be sent). The I2C BUSY flag cannot be cleared by the SWRST
control bit, nor by a peripheral or a system reset. BUSY bit is cleared under reset, but it is
set high again as soon as the reset is released, because the analog filter output is still at low
level. This issue occurs randomly.
Note: Under the same conditions, the I2C analog filters may also provide a high level, whereas
SCL and SDA lines are kept to low level. This should not create issues as the filters output
will be correct after next SCL and SDA transition.
Возникает при сильных помехах на I2C (кто-то туда полез тыкать отверткой или пинцетом и куда попало коротить). Решается программно.
Workaround
The SCL and SDA analog filter output is updated after a transition occurs on the SCL and
SDA line respectively. The SCL and SDA transition can be forced by software configuring
the I2C I/Os in output mode. Then, once the analog filters are unlocked and output the SCL
and SDA lines level, the BUSY flag can be reset with a software reset, and the I2C can enter
master mode. Therefore, the following sequence must be applied:
1. Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register.
2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level
(Write 1 to GPIOx_ODR).
3. Check SCL and SDA High level in GPIOx_IDR.
4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
5. Check SDA Low level in GPIOx_IDR.
6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
7. Check SCL Low level in GPIOx_IDR.
8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to
GPIOx_ODR).
9. Check SCL High level in GPIOx_IDR.
10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to
GPIOx_ODR).
11. Check SDA High level in GPIOx_IDR.
12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
13. Set SWRST bit in I2Cx_CR1 register.
14. Clear SWRST bit in I2Cx_CR1 register.
15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register.
Я не считаю что из-за этого стоит отказываться от МК.[/uquote]

Добавлено after 1 minute 6 seconds:
Каким образом это можно реализовать.До настроек битов шины или как?

Добавлено after 17 minutes 4 seconds:
[uquote="a797945",url="/forum/viewtopic.php?p=3911510#p3911510"]перепишите эту процедуру в понятных Вам терминах и Вы поймете что к параметрам инита это никакого отношения не имеет.
протокол проверки линий перед тем как вновь поднять интерфейс.[/uquote]
Вы имеете ввиду.Перед СтартоМ?
Реклама
Эиком - электронные компоненты и радиодетали
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

И ещё один вопрос Check SCL and SDA High level in GPIOx_IDR.
4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
5. Check SDA Low level in GPIOx_IDR.
6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
7. Check SCL Low level in GPIOx_IDR.
Это что это SDA SCL выходы на входы настроить?
Реклама
Аватара пользователя
Zhuk72
Сверлит текстолит когтями
Сообщения: 1231
Зарегистрирован: Ср янв 29, 2014 08:41:31
Откуда: Баку
Контактная информация:

Re: stm32f1 cmsis настройка I2C

Сообщение Zhuk72 »

Там же написано "Output open-drain". Какой ещё вход?
Каждый имеет право на свое личное ошибочное мнение.

У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
Реклама
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

l in GPIOx_IDR регистр входа? Или я не так понимаю?
a797945
Мучитель микросхем
Сообщения: 446
Зарегистрирован: Вс ноя 01, 2015 09:15:16
Откуда: 69.Ржев

Re: stm32f1 cmsis настройка I2C

Сообщение a797945 »

уже сколько раз у Вас спросили - Вы rm на камень вообще открывали?

раздел General-purpose and alternate-function I/Os (GPIOs and AFIOs)
(есть картинки)
SDA, SCL - линии системы i2c
в кубе или в даташите на конкретный камень можно увидеть на какие ножки мс приходятся.
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

Открывал конечно.GPIOx_IDR но вот это мне не совсем понятно.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f1 cmsis настройка I2C

Сообщение VladislavS »

И что конкретно непонятно? Буквы незнакомые?
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

Это регистр входа .А у нас альтернативные выходы.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f1 cmsis настройка I2C

Сообщение VladislavS »

Вы где-то видите отключение IDR от входа в AF?
Изображение
image_2020-10-25_214958.png
(81.66 КБ) 1560 скачиваний
a797945
Мучитель микросхем
Сообщения: 446
Зарегистрирован: Вс ноя 01, 2015 09:15:16
Откуда: 69.Ржев

Re: stm32f1 cmsis настройка I2C

Сообщение a797945 »

ну отключите АФ на эти ноги,
для работы i2c все равно включите, согласно п.12 этой процедуры.
я так понимаю у Вас не складывается дружба с i2c, и Вы почему-то решили что эта процедура Вам должна помочь, а то что она для решения конкретной (и скорее всего не Вашей) ситуации Вас не интересует.
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

всё равно не работает. RCC->APB2ENR|=RCC_APB2ENR_IOPBEN ;
GPIOB->CRL|= GPIO_CRL_CNF7_1 | GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_0 | GPIO_CRL_CNF6_0 |
GPIO_CRL_MODE7_1 | GPIO_CRL_MODE6_1 | GPIO_CRL_MODE7_0 | GPIO_CRL_MODE6_0;
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
RCC->APB1ENR|= RCC_APB1ENR_I2C1EN;
I2C1->CR1&=~I2C_CR1_SMBUS ;
I2C1->CCR &=~I2C_CCR_FS;


CLEAR_BIT(I2C1->CR1, I2C_CR1_NOSTRETCH);




I2C1->CR2 |=_VAL2FLD(I2C_CR2_FREQ,36);

I2C1->CCR=180;
I2C1->TRISE= 37;
I2C1->OAR1 = I2C_OWNADDRESS1_7BIT;//(36MHz/100KHz/2)
//(1mcs/(1/36MHz)+1)

I2C1->CR1|= I2C_CR1_ACK;
I2C1->CR1|=I2C_CR1_PE;
I2C1->CR1&=~I2C_CR1_PE;
GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_CNF7);
GPIOB->CRL |= GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0;//2

GPIOB->ODR |= GPIO_ODR_ODR6 | GPIO_ODR_ODR7;
while(!(GPIOB->IDR &(GPIO_IDR_IDR6 | GPIO_IDR_IDR7)));//3

GPIOB->ODR &= ~(GPIO_ODR_ODR7);//4
while(GPIOB->IDR & GPIO_IDR_IDR7 );//5

GPIOB->ODR &= ~(GPIO_ODR_ODR6);//6
while(GPIOB->IDR & GPIO_IDR_IDR6);//7

GPIOB->ODR |= GPIO_ODR_ODR6;//8
while(!(GPIOB->IDR & GPIO_IDR_IDR6));//9

GPIOB->ODR |= GPIO_ODR_ODR7;//10
while(!(GPIOB->IDR & GPIO_IDR_IDR7));//11
//

// PB6 SCL pin
GPIOB->CRL|=GPIO_CRL_CNF6; // 12 alternate function open drain
GPIOB->CRL|=GPIO_CRL_MODE6; // max speed 50MHz

// PB7 SDA pin
GPIOB->CRL|=GPIO_CRL_CNF7; // alternate function open drain
GPIOB->CRL|=GPIO_CRL_MODE7; // max speed 50MHz



I2C1->CR1 |= I2C_CR1_SWRST;//13
I2C1->CR1 &= ~(I2C_CR1_SWRST);//1

SET_BIT(I2C1->CR1, I2C_CR1_PE);
} void RCC_init(void)
{
FLASH->ACR |=FLASH_ACR_PRFTBE ;
FLASH->ACR|=FLASH_ACR_LATENCY_2;

RCC->CR |=RCC_CR_HSEON ;
while(!(RCC->CR&RCC_CR_HSERDY ))
{

}
RCC->CR |=RCC_CR_CSSON ;
RCC->CFGR|=RCC_CFGR_PLLSRC;
RCC->CFGR|=RCC_CFGR_PLLXTPRE_HSE;

RCC->CFGR=RCC_CFGR_PLLMULL9;
RCC->CFGR|=RCC_CFGR_HPRE_DIV1; //_VAL2FLD(RCC_CFGR_HPRE,1);
RCC->CFGR|=RCC_CFGR_PPRE1_DIV2 ;//_VAL2FLD(RCC_CFGR_PPRE1,2);
RCC->CFGR|=RCC_CFGR_PPRE2_DIV1; //_VAL2FLD(RCC_CFGR_PPRE2,1);
RCC->CFGR|= RCC_CFGR_ADCPRE_DIV2;
RCC->CR|=RCC_CR_PLLON;
while(!(RCC->CR&RCC_CR_PLLRDY))
{

}

RCC->CFGR|=RCC_CFGR_SW_PLL;
while(!(RCC->CFGR&RCC_CFGR_SWS_PLL ))
{

}
}

Код: Выделить всё

[list][/list]
void NMI_Handler(void)
{
if((RCC->CIR & RCC_CIR_CSSF) != 0) // HSE fail
{
RCC->CIR |= RCC_CIR_CSSC; // Clear CSSF flag

}
}
вот такая тактовая частота.Я знаю что проблема в настройки шины .Но что я ещё не учёл ? Не знаю .Да и stm32f4 без заморочек не запускается.Котики помогите .Что упускаю?Какую настройку?

Добавлено after 5 minutes 29 seconds: #define I2C_REQUEST_WRITE 0x00
#define I2C_REQUEST_READ 0x01
#define I2C_OWNADDRESS1_7BIT 0x00004000U
#define I2C_MODE_I2C 0x00000000U
//------------------------------------------------
__IO uint32_t tmpreg1;
//------------------------------------------------
void I2C_SendByteByADDR(I2C_TypeDef * i2c, uint8_t c,uint8_t addr)
{

i2c->CR1&=~ I2C_CR1_POS;
i2c->CR1|= I2C_CR1_ACK;
i2c->CR1|= I2C_CR1_START;
while (!(i2c->SR1& I2C_SR1_SB)){};
(void) i2c->SR1;

i2c->DR =addr | I2C_REQUEST_WRITE;
while (!(i2c->SR1& I2C_SR1_ADDR)){};
(void) i2c->SR1;
(void) i2c->SR2;

i2c->DR=c;
while (!(i2c->SR1& I2C_SR1_TXE)){};

i2c->CR1|= I2C_CR1_STOP;
}
Функция отправки байта. Она работает .Проверенно в кубе ll библиотека.
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

АУ вы где.? За мышами побижали?
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f1 cmsis настройка I2C

Сообщение ivan dimir »

[uquote="a797945",url="/forum/viewtopic.php?p=3912662#p3912662"]ну отключите АФ на эти ноги,
для работы i2c все равно включите, согласно п.12 этой процедуры.
я так понимаю у Вас не складывается дружба с i2c, и Вы почему-то решили что эта процедура Вам должна помочь, а то что она для решения конкретной (и скорее всего не Вашей) ситуации Вас не интересует.[/uquote]
На stm32f407 всё получилось.А на stm32f103 пока 0.Я три недели болел ковид. int main(void)
{

RCC_init();
init_systimer1();

APB1_GPIO_init();
GPIO_inits();
// I2C_Init();
//LCD_ini();
I2C_Init();
// RCC->APB1ENR&=~ RCC_APB1ENR_I2C1EN;
RCC->APB2ENR &=~RCC_APB2ENR_AFIOEN;
I2C1->CR1&=~I2C_CR1_PE;
GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_CNF7);
GPIOB->CRL |= GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0;//2

GPIOB->ODR |= GPIO_ODR_ODR6 | GPIO_ODR_ODR7;
while(!(GPIOB->IDR &(GPIO_IDR_IDR6 | GPIO_IDR_IDR7)));//3

GPIOB->ODR &= ~(GPIO_ODR_ODR7);//4
while(GPIOB->IDR & GPIO_IDR_IDR7 );//5

GPIOB->ODR &= ~(GPIO_ODR_ODR6);//6
while(GPIOB->IDR & GPIO_IDR_IDR6);//7

GPIOB->ODR |= GPIO_ODR_ODR6;//8
while(!(GPIOB->IDR & GPIO_IDR_IDR6));//9

GPIOB->ODR |= GPIO_ODR_ODR7;//10
while(!(GPIOB->IDR & GPIO_IDR_IDR7));//11
//

// PB6 SCL pin
// RCC->APB2ENR |=RCC_APB2ENR_AFIOEN;
// RCC->APB1ENR=RCC_APB1ENR_I2C1EN;
RCC->APB2ENR |=RCC_APB2ENR_AFIOEN;
GPIOB->CRL|=GPIO_CRL_CNF6; // 12 alternate function open drain
GPIOB->CRL|=GPIO_CRL_MODE6; // max speed 50MHz

// PB7 SDA pin
GPIOB->CRL|=GPIO_CRL_CNF7; // alternate function open drain
GPIOB->CRL|=GPIO_CRL_MODE7; // max speed 50MHz



I2C1->CR1 |= I2C_CR1_SWRST;//13
I2C1->CR1 &= ~I2C_CR1_SWRST;//1
// RCC->APB1RSTR|=RCC_APB1RSTR_I2C1RST ;
// RCC->APB1RSTR&=~RCC_APB1RSTR_I2C1RST ;
SET_BIT(I2C1->CR1, I2C_CR1_PE);
timer_1();
timer_3();
//init_systimer1();

init_ADC1();

LCD_ini();
// LCD_Clear();

LCD_SetPos(5,1);
LCD_String("String 2");
ADC1->CR2|=ADC_CR2_JEXTSEL_1; #include "i2c_user.h"
#include"main.h"
//------------------------------------------------
#define I2C_REQUEST_WRITE 0x00
#define I2C_REQUEST_READ 0x01
#define I2C_OWNADDRESS1_7BIT 0x00004000U
#define I2C_MODE_I2C 0x00000000U
//------------------------------------------------
__IO uint32_t tmpreg1;
//------------------------------------------------
void I2C_SendByteByADDR(I2C_TypeDef * i2c, uint8_t c,uint8_t addr)
{

i2c->CR1&=~I2C_CR1_POS;
i2c->CR1|= I2C_CR1_ACK;
i2c->CR1|= I2C_CR1_START;
while (!(i2c->SR1& I2C_SR1_SB)){};
(void) i2c->SR1;
i2c->DR|= addr | I2C_REQUEST_WRITE;
while (!(i2c->SR1& I2C_SR1_ADDR)){};
(void) i2c->SR1;
(void) i2c->SR2;
//I2C_Write_Byte(c);
i2c->DR|= c;
while (!(i2c->SR1&I2C_SR1_TXE)){};
i2c->CR1|= I2C_CR1_STOP;
}
//------------------------------------------------
void I2C_Init(void)
{
//I2C1 GPIO
RCC->APB2ENR|=RCC_APB2ENR_IOPBEN ;
GPIOB->CRL|= GPIO_CRL_CNF7_1 | GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_0 | GPIO_CRL_CNF6_0 |
GPIO_CRL_MODE7_1 | GPIO_CRL_MODE6_1 | GPIO_CRL_MODE7_0 | GPIO_CRL_MODE6_0;
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
RCC->APB1ENR|= RCC_APB1ENR_I2C1EN;
I2C1->CR1&=~I2C_CR1_SMBUS ;
I2C1->CCR &=~I2C_CCR_FS;


CLEAR_BIT(I2C1->CR1, I2C_CR1_NOSTRETCH);




I2C1->CR2 |=_VAL2FLD(I2C_CR2_FREQ,36);

I2C1->CCR|=180;
I2C1->TRISE|= 37;
I2C1->OAR1 |= I2C_OWNADDRESS1_7BIT;//(36MHz/100KHz/2)
//(1mcs/(1/36MHz)+1)

I2C1->CR1|= I2C_CR1_ACK;
I2C1->CR1|=I2C_CR1_PE;
}
Не работает.Что не хватает?
Аватара пользователя
GARMIN
Держит паяльник хвостом
Сообщения: 954
Зарегистрирован: Вс дек 02, 2012 16:58:33
Откуда: от туда
Контактная информация:

Re: stm32f1 cmsis настройка I2C

Сообщение GARMIN »

Я тоже немного приболел, и не могу проверить ваш код.
Вот рабочий код из рабочего проекта:
Спойлер

Код: Выделить всё

/*******************************************************************************
// Wait until I2C flag cleared
// input:
//   I2C_Flag - I2C flag (one of I2C_F_XXX values)
// return:
//   SUCCESS if flag cleared or ERROR in case of timeout
*******************************************************************************/
ErrorStatus wait_flag_reset (volatile u16 *reg, u16 Flag) 
{
  u32 timer;
  
  timer_reset (&timer);
	while (timer_active (&timer, 3)) // Wait until flag cleared
  {
		if (!(*reg & Flag)) return SUCCESS;
	}
	return ERROR;
}

ErrorStatus i2c_init (void)
{
  const u32 i2c_APB_freq = 36;  // частота шины APB 32МГц
  const u32 i2c_freq = 400; // частота i2c= 400 кГц
  // CCR = 45, это кол-во тактов APB1 на полупериод SCL (36МГц/(400КГц * 2))
//  u32 i2c_ccr = 180;  // 36MHz / (2 * 100kHz) = 180
  const u32 i2c_ccr = (i2c_APB_freq * 1000) / (2 * i2c_freq);  // i2c_APB_freq / (2 * 400kHz) = 45
  // TRISE = 9, это кол-во тактов APB1, через которое проверяется состояние SCL 
  // при переходе в откл. состояние (из 0 в 1), 
  // это время должно быть чуть больше 1 мкс (1мкс/(1/i2c_APB_freq)+1) = i2c_APB_freq + 1
  const u32 t_rise = i2c_APB_freq + 1;
  
//------------------------Грабли из Errata------------------------------------//
//1.Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register.
   I2C1->CR1 &= ~I2C_CR1_PE;
//2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).
  GPIOB->CRL = GPIO_CRL_MODE0_INPUT     | GPIO_CRL_CNF0_IN_FL
            | GPIO_CRL_MODE1_INPUT      | GPIO_CRL_CNF1_IN_FL
            | GPIO_CRL_MODE2_INPUT      | GPIO_CRL_CNF2_IN_FL
            | GPIO_CRL_MODE3_INPUT      | GPIO_CRL_CNF3_IN_FL
            | GPIO_CRL_MODE4_INPUT      | GPIO_CRL_CNF4_IN_FL
            | GPIO_CRL_MODE5_INPUT      | GPIO_CRL_CNF5_IN_FL
            | GPIO_CRL_MODE6_OUTPUT10M  | GPIO_CRL_CNF6_OUT_GP_PP
            | GPIO_CRL_MODE7_OUTPUT10M  | GPIO_CRL_CNF7_OUT_GP_PP;
  GPIOB->BSRR = GPIO_BSRR_BS6 | GPIO_BSRR_BS7;
//3. Check SCL and SDA High level in GPIOx_IDR.
  wait_flag_set ((u16 *)GPIOB->IDR, GPIO_IDR_IDR6 | GPIO_IDR_IDR7);
//  while (!(GPIOB->IDR &(GPIO_IDR_IDR6 | GPIO_IDR_IDR7)));
//4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).
  GPIOB->CRL = GPIO_CRL_MODE0_INPUT     | GPIO_CRL_CNF0_IN_FL
            | GPIO_CRL_MODE1_INPUT      | GPIO_CRL_CNF1_IN_FL
            | GPIO_CRL_MODE2_INPUT      | GPIO_CRL_CNF2_IN_FL
            | GPIO_CRL_MODE3_INPUT      | GPIO_CRL_CNF3_IN_FL
            | GPIO_CRL_MODE4_INPUT      | GPIO_CRL_CNF4_IN_FL
            | GPIO_CRL_MODE5_INPUT      | GPIO_CRL_CNF5_IN_FL
            | GPIO_CRL_MODE6_OUTPUT10M  | GPIO_CRL_CNF6_OUT_GP_PP
            | GPIO_CRL_MODE7_OUTPUT10M  | GPIO_CRL_CNF7_OUT_GP_OD;
  GPIOB->BRR = GPIO_BRR_BR7;
//5. Check SDA Low level in GPIOx_IDR.
  wait_flag_reset ((u16 *)GPIOB->IDR, GPIO_IDR_IDR7);
//  while (GPIOB->IDR & GPIO_IDR_IDR7);
//6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).
  GPIOB->CRL = GPIO_CRL_MODE0_INPUT     | GPIO_CRL_CNF0_IN_FL
            | GPIO_CRL_MODE1_INPUT      | GPIO_CRL_CNF1_IN_FL
            | GPIO_CRL_MODE2_INPUT      | GPIO_CRL_CNF2_IN_FL
            | GPIO_CRL_MODE3_INPUT      | GPIO_CRL_CNF3_IN_FL
            | GPIO_CRL_MODE4_INPUT      | GPIO_CRL_CNF4_IN_FL
            | GPIO_CRL_MODE5_INPUT      | GPIO_CRL_CNF5_IN_FL
            | GPIO_CRL_MODE6_OUTPUT10M  | GPIO_CRL_CNF6_OUT_GP_OD
            | GPIO_CRL_MODE7_OUTPUT10M  | GPIO_CRL_CNF7_OUT_GP_OD;
  GPIOB->BRR = GPIO_BRR_BR6;
//7. Check SCL Low level in GPIOx_IDR.
  wait_flag_reset ((u16 *)GPIOB->IDR, GPIO_IDR_IDR6);
//  while (GPIOB->IDR & GPIO_IDR_IDR6);
//8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).
  GPIOB->BSRR = GPIO_BSRR_BS6;
//9. Check SCL High level in GPIOx_IDR.
  wait_flag_set ((u16 *)GPIOB->IDR, GPIO_IDR_IDR6);
//  while (!(GPIOB->IDR & GPIO_IDR_IDR6));
//10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to GPIOx_ODR).
  GPIOB->BSRR = GPIO_BSRR_BS7;
//11. Check SDA High level in GPIOx_IDR.
  wait_flag_set ((u16 *)GPIOB->IDR, GPIO_IDR_IDR7);
//  while (!(GPIOB->IDR & GPIO_IDR_IDR7));
//12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
  GPIOB->CRL = GPIO_CRL_MODE0_INPUT     | GPIO_CRL_CNF0_IN_FL
            | GPIO_CRL_MODE1_INPUT      | GPIO_CRL_CNF1_IN_FL
            | GPIO_CRL_MODE2_INPUT      | GPIO_CRL_CNF2_IN_FL
            | GPIO_CRL_MODE3_INPUT      | GPIO_CRL_CNF3_IN_FL
            | GPIO_CRL_MODE4_INPUT      | GPIO_CRL_CNF4_IN_FL
            | GPIO_CRL_MODE5_INPUT      | GPIO_CRL_CNF5_IN_FL
            | GPIO_CRL_MODE6_OUTPUT10M  | GPIO_CRL_CNF6_OUT_AF_OD
            | GPIO_CRL_MODE7_OUTPUT10M  | GPIO_CRL_CNF7_OUT_AF_OD;
//13. Set SWRST bit in I2Cx_CR1 register.
  I2C1->CR1 = I2C_CR1_SWRST;
//14. Clear SWRST bit in I2Cx_CR1 register.
  I2C1->CR1 = 0;
//15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register.
//----------------------------------------------------------------------------//
  I2C1->CR2 = (I2C_CR2_FREQ & (i2c_APB_freq / 2)) // Peripheral Clock Frequency in MHz */
        | I2C_CR2_ITERREN   * 0   // Error Interrupt Enable 
        | I2C_CR2_ITEVTEN   * 0   // Event Interrupt Enable 
        | I2C_CR2_ITBUFEN   * 0   // Buffer Interrupt Enable
        | I2C_CR2_DMAEN     * 0   // DMA Requests Enable
        | I2C_CR2_LAST      * 0;  // DMA Last Transfer 
  
  I2C1->TRISE = t_rise; 
  
  I2C1->CCR = (I2C_CCR_CCR & i2c_ccr)
        | I2C_CCR_FS_F;   // Fm mode 400 kHz
  // Set I2C own address: 0x00, 7-bit
	I2C1->OAR1 = (1 << 14); // Bit 14 should be kept as 1
  
  I2C1->CR1 = I2C_CR1_PE    * 1   // Peripheral Enable 
        | I2C_CR1_SMBUS     * 0   // SMBus Mode 
        | I2C_CR1_SMBTYPE   * 0   // SMBus Type 
        | I2C_CR1_ENARP     * 0   // ARP Enable 
        | I2C_CR1_ENPEC     * 0   // PEC Enable 
        | I2C_CR1_ENGC      * 0   // General Call Enable 
        | I2C_CR1_NOSTRETCH * 0   // Clock Stretching Disable (Slave mode) 
        | I2C_CR1_START     * 0   // Start Generation 
        | I2C_CR1_STOP      * 0   // Stop Generation 
        | I2C_CR1_ACK       * 1   // Acknowledge Enable 
        | I2C_CR1_POS       * 0   // Acknowledge/PEC Position (for data reception)
        | I2C_CR1_PEC       * 0   // Packet Error Checking
        | I2C_CR1_ALERT     * 0   // SMBus Alert
        | I2C_CR1_SWRST     * 0;  // Software Reset

	// Wait until I2C bus is free
  ErrorStatus stat;
  stat = wait_flag_reset (&I2C1->SR2, I2C_SR2_BUSY);
  return stat;
}
Запись и чтение 8 и 16 бит данных:
Спойлер

Код: Выделить всё

ErrorStatus i2c_write (u8 addr, u8 *cmd, u16 len, u8 cont)
{
  I2C1->CR1 |= I2C_CR1_START;//стартуем
  if (wait_flag_set (&I2C1->SR1, I2C_SR1_SB) == ERROR) return ERROR;  // Wait for EV5

  I2C1->DR = addr & ~0x01; //передаем адрес устройства, бит 0 = 0 (запись)
  if (wait_flag_set (&I2C1->SR1, I2C_SR1_ADDR) == ERROR) return ERROR;  // Wait for EV6
  if (I2C1->SR1 & I2C_SR1_AF) return ERROR;  // if NACK
  (void) I2C1->SR1;  // clear ADDR
  (void) I2C1->SR2;  // clear ADDR

	I2C1->DR = *cmd++;  // Send first byte (EV8)
	while (--len)       // Send rest of data (if present)
  {
		if (wait_flag_set (&I2C1->SR1, I2C_SR1_TXE) == ERROR) return ERROR; // Wait for BTF flag set
		I2C1->DR = *cmd++;// Transmit byte via I2C
	}
	if (wait_flag_set (&I2C1->SR1, I2C_SR1_TXE | I2C_SR1_BTF) == ERROR) return ERROR; // Wait for BTF flag set

  if (!cont)
  {
    I2C1->CR1 |= I2C_CR1_STOP;
    if (wait_flag_reset (&I2C1->SR1, I2C_SR1_STOPF) == ERROR) return ERROR;
  } 
  return SUCCESS;
}


ErrorStatus i2c_read (u8 addr, u8 *data, u16 len)
{
	I2C1->CR1 |= I2C_CR1_START; //рестарт!!!
	if (wait_flag_set (&I2C1->SR1, I2C_SR1_SB) == ERROR) return ERROR;  // Wait for EV5

	//передаем адрес устройства, но теперь для чтения
	I2C1->DR = addr | 0x01;
	if (wait_flag_set (&I2C1->SR1, I2C_SR1_ADDR) == ERROR) return ERROR; // Wait for EV6
  
	// There are can be three cases:
	//   read 1 byte
	//   read 2 bytes
	//   read more than 2 bytes
	if (len == 1) // Receive 1 byte (AN2824 figure 2)
  {
		I2C1->CR1 &= ~I2C_CR1_ACK;  // Disable I2C acknowledgment	
		__disable_irq();            // EV6_1 must be atomic operation (AN2824)
		(void) I2C1->SR1;           // Clear ADDR bit
		(void) I2C1->SR2;
		I2C1->CR1 |= I2C_CR1_STOP;  // Generate a STOP condition
		__enable_irq();
		// Wait for RxNE flag (receive buffer not empty) EV7
		if (wait_flag_set (&I2C1->SR1, I2C_SR1_RXNE) == ERROR) return ERROR;
		*data = (u8)I2C1->DR;       // Read received byte
	} 
  else if (len == 2) // Receive 2 bytes (AN2824 figure 2)
  {
		I2C1->CR1 |= I2C_CR1_POS;   // Set POS flag (NACK position next)
		__disable_irq();            // EV6_1 must be atomic operation (AN2824)
    (void) I2C1->SR1;
		(void) I2C1->SR2;           // Clear ADDR bit
		I2C1->CR1 &= ~I2C_CR1_ACK;  // Disable I2C acknowledgment
		__enable_irq();
		// Wait for BTF flag set (byte transfer finished) EV7_3
		if (wait_flag_set (&I2C1->SR1, I2C_SR1_BTF) == ERROR) return ERROR;
		__disable_irq();            // This should be atomic operation
		I2C1->CR1 |= I2C_CR1_STOP;  // Generate a STOP condition
		*data++ = (u8)I2C1->DR;     // Read first received byte
		__enable_irq(); 
		*data = (u8)I2C1->DR;       // Read second received byte
   I2C1->CR1 &= ~I2C_CR1_POS;   // Clear POS flag // NACK position current
	} 
  else // Receive more than 2 bytes (AN2824 figure 1)
  {
    (void) I2C1->SR1;
		(void) I2C1->SR2;           // Clear ADDR bit
		while (len-- > 2)          // Read received bytes into buffer
    {
			// Wait for BTF (cannot guarantee 1 transfer completion time)
			if (wait_flag_set (&I2C1->SR1, I2C_SR1_BTF) == ERROR) return ERROR;
			*data++ = (u8)I2C1->DR;
		}
		// Wait for BTF flag set (byte transfer finished) EV7_2
		if (wait_flag_set (&I2C1->SR1, I2C_SR1_BTF) == ERROR) return ERROR;
		I2C1->CR1 &= ~I2C_CR1_ACK;  // Disable the I2C acknowledgment
		__disable_irq();
		I2C1->CR1 |= I2C_CR1_STOP;  // Generate a STOP condition
		*data++ = (u8)I2C1->DR;     // Read received byte N-1
		__enable_irq();
		// Wait for last byte received
	  if (wait_flag_set (&I2C1->SR1, I2C_SR1_RXNE) == ERROR) return ERROR;
		*data = (u8)I2C1->DR;// Read last received byte
	}
	// Wait for a STOP flag
	if (wait_flag_reset (&I2C1->SR1, I2C_SR1_STOPF) == ERROR) return ERROR;
  I2C1->CR1 |= I2C_CR1_ACK;   // Enable Acknowledgment
  
	return SUCCESS;	
}


ErrorStatus i2c_read16 (u8 addr, u16 *data, u16 len)
{
  u16 first_byte;
  u16 second_byte;
  
	I2C1->CR1 |= I2C_CR1_START; //рестарт!!!
	if (wait_flag_set (&I2C1->SR1, I2C_SR1_SB) == ERROR) return ERROR; // Wait for EV5
	//передаем адрес устройства, но теперь для чтения
	I2C1->DR = addr | 0x01;
	if (wait_flag_set (&I2C1->SR1, I2C_SR1_ADDR) == ERROR) return ERROR; // Wait for EV6
  
  if (len == 1) // Receive only 2 bytes (AN2824 figure 2)
  {
    I2C1->CR1 |= I2C_CR1_POS;       // Set POS flag (NACK position next)
    __disable_irq();                // This should be atomic operation
    (void) I2C1->SR1;               
    (void) I2C1->SR2;               // reset ADDR
    I2C1->CR1 &= ~I2C_CR1_ACK;      // NACK, Disable the I2C acknowledgment
    __enable_irq();
    // Wait for BTF flag set (byte transfer finished) EV7_3
    if (wait_flag_set (&I2C1->SR1, I2C_SR1_BTF) == ERROR) return ERROR;
    __disable_irq();                // This should be atomic operation
    I2C1->CR1 |= I2C_CR1_STOP;      // Generate a STOP condition
    first_byte = I2C1->DR << 8;  // Read received byte N-1
    __enable_irq();
    second_byte = I2C1->DR;      // Read second received byte
    *data = (first_byte << 8) | second_byte;
    // Wait for a STOP flag
    if (wait_flag_reset (&I2C1->SR1, I2C_SR1_STOPF) == ERROR) return ERROR;
    I2C1->CR1 &= ~I2C_CR1_POS;  // Clear POS flag // NACK position current
  }
  else
  {
    (void) I2C1->SR1;                       
    (void) I2C1->SR2;                       // reset ADDR
    while (--len)
    {
      if (wait_flag_set (&I2C1->SR1, I2C_SR1_RXNE) == ERROR) return ERROR;
      first_byte = I2C1->DR;
      if (wait_flag_set (&I2C1->SR1, I2C_SR1_RXNE) == ERROR) return ERROR;
      second_byte = I2C1->DR;
      *data++ = (first_byte << 8) | second_byte;
    }
    // receive last two bytes
    if (wait_flag_set (&I2C1->SR1, I2C_SR1_RXNE) == ERROR) return ERROR;    
    I2C1->CR1 &= ~I2C_CR1_ACK;      // NACK, Disable the I2C acknowledgment
    __disable_irq();                // This should be atomic operation
    I2C1->CR1 |= I2C_CR1_STOP;      // Generate a STOP condition
    first_byte = I2C1->DR;          // Read received byte N-1
    __enable_irq();
    
    if (wait_flag_set (&I2C1->SR1, I2C_SR1_RXNE) == ERROR) return ERROR;
    second_byte = I2C1->DR;      // Read second received byte
    // Wait for a STOP flag
    if (wait_flag_reset (&I2C1->SR1, I2C_SR1_STOPF) == ERROR) return ERROR;
    *data = (first_byte << 8) | second_byte;
  }
  I2C1->CR1 |= I2C_CR1_ACK;   // Enable Acknowledgment
	return SUCCESS;	
}
Ответить

Вернуться в «ARM»