не работает I2C STM32F030
31.08.2020, 09:42. Показов 2663. Ответов 1
Проц STM32F030K6 под Hal на этой платке все работает, но если честно хочется написать на CMSIS. Порты выбраны везде одинаковые ножки тоже, конфиг ног вроде тоже одинаковый. Перепробовал кучу различных библиотек и различные настройки таймингов, но результат нулевой. Анализатор вообще не видит посылки пакетов, а осциллограф показывает странные уровни, либо это от 3 вольт идет пачка "импульсов" в которой каждый следующий импульс спускается примерно на пол вольта (верх 3 низ 2,5 у следующего 2,5верх низ 2 ну и тд.) либо импульсы "нормальные" но уровень не поднимается выше вольта. Подтяжка есть физическая и обычную включал. Подозреваю, что либо где то забыл включить тактирование (но сравнивал), либо с настройкой тайминга что то не то (высчитывал в Хале)
i2c.c | C | 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
| #define TIMING_I2C_REG 0x00201D2B // normal 100kHz 100ns Rise 100 Fall tact 8MHz hsi
typedef enum _I2C_Direction {I2C_Transmitter=0, I2C_Receiver=1} I2C_Direction;
void i2c_Init2(void)
{
// Включить тактирование модуля I2C1
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
// Настроим выводы PB9, PB10
// Open drain!
GPIOA->OTYPER |= GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_10;
GPIOA->OSPEEDR |= (GPIO_OSPEEDR_OSPEEDR9 | GPIO_OSPEEDR_OSPEEDR10);
GPIOA->PUPDR |= (GPIO_PUPDR_PUPDR9_0 | GPIO_PUPDR_PUPDR10_0);
// Номер альтернативной функции
GPIOA->AFR[1] |= ((0x04UL<<GPIO_AFRH_AFSEL9_Pos) | (0x04UL<<GPIO_AFRH_AFSEL10_Pos));// В 9 & 10 pin запишем 4
GPIOA->MODER |= ((0x02UL << GPIO_MODER_MODER9_Pos) | (0x02UL << GPIO_MODER_MODER10_Pos)); // В 9, 10 запишем 2
// select HSI or SYSCLK
RCC->CFGR3 &= ~(0x1U << RCC_CFGR3_I2C1SW_SYSCLK_Pos);
I2C1->CR1 = 0;
I2C1->TIMINGR = (uint32_t)TIMING_I2C_REG;
// Включим модуль
I2C1->CR1 |= (I2C_CR1_PE); //enable Peripheral
while ((I2C_BUS->CR1 & I2C_CR1_PE)==0) {}; // Жду пока включится
// Теперь можно что-нибудь делать
}
void I2C_Start_Direction_Adress_Size (I2C_Direction Direction, uint8_t Adress, uint8_t Size)
{
//I2C_BUS->CR2 &= ~I2C_CR2_AUTOEND; // Выдавать стоп вручную
//I2C_BUS->CR2 &= ~I2C_CR2_RELOAD; // Не использовать режим перезагрузки
if (Direction) I2C_BUS->CR2 |= I2C_CR2_RD_WRN; // Режим приёма
else I2C_BUS->CR2 &= ~I2C_CR2_RD_WRN; // Режим передачи
I2C_BUS->CR2 &= ~I2C_CR2_NBYTES; // Очистить размер данных
I2C_BUS->CR2 |= Size<<I2C_OFFSET_CR2_NBYTES; // Установить размер данных
I2C_BUS->CR2 &= ~I2C_CR2_SADD; // Очистить адрес ведомого устройства
I2C_BUS->CR2 |= Adress; // Установить адрес ведомого устройства
I2C_BUS->CR2 |= I2C_CR2_START; // Выдать старт на шину
while ((I2C_BUS->ISR & I2C_ISR_BUSY)!=0) {}; // Ожидать выдачу старта
}
void I2C_Stop (void)
{
while(!(I2C1->ISR & I2C_ISR_TXE)){}; // TX buf Empty добавка с форума
I2C_BUS->CR2 |= I2C_CR2_STOP; // Выдать стоп на шину
while (I2C_BUS->ISR & I2C_ISR_BUSY) {}; // Ожидать выдачу стопа
// Очищаю флаги - необходимо для дальнейшей работы шины
I2C_BUS->ICR |= I2C_ICR_STOPCF; // STOP флаг
I2C_BUS->ICR |= I2C_ICR_NACKCF; // NACK флаг
// Если есть ошибки на шине - очищаю флаги
if (I2C_BUS->ISR & (I2C_ISR_ARLO | I2C_ISR_BERR))
{
I2C_BUS->ICR |= I2C_ICR_ARLOCF;
I2C_BUS->ICR |= I2C_ICR_BERRCF;
}
}
uint8_t I2C_Write_Transaction (uint8_t Adress, uint8_t Register, uint8_t *Data, uint8_t Size)
{
uint8_t Count=0; // Счётчик успешно переданных байт
// Старт
I2C_Start_Direction_Adress_Size (I2C_Transmitter, Adress, 1+Size);
// Сейчас либо I2C запросит первый байт для отправки,
// Либо взлетит NACK-флаг, говорящий о том, что микросхема не отвечает.
// Если взлетит NACK-флаг, отправку прекращаем.
while ((((I2C_BUS->ISR & I2C_ISR_TXIS)==0) && ((I2C_BUS->ISR & I2C_ISR_NACKF)==0)) && (I2C_BUS->ISR & I2C_ISR_BUSY)) {};
if (I2C_BUS->ISR & I2C_ISR_TXIS) I2C_BUS->TXDR=Register; // Отправляю адрес регистра
// Отправляем байты до тех пор, пока не взлетит TC-флаг.
// Если взлетит NACK-флаг, отправку прекращаем.
while ((((I2C_BUS->ISR & I2C_ISR_TC)==0) && ((I2C_BUS->ISR & I2C_ISR_NACKF)==0)) && (I2C_BUS->ISR & I2C_ISR_BUSY))
{
if (I2C_BUS->ISR & I2C_ISR_TXIS) I2C_BUS->TXDR=*(Data+Count++); // Отправляю данные
}
I2C_Stop();
if (Count == Size) return 1; return 0;
} |
|
clock_cfg.h
| C | 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
| #include "stm32f0xx.h" // Device header
#ifndef CLOCK_CFG_H
#define CLOCK_CFG_H
//#define EXT_CRYSTAL_EN // uncomment for enable external quartz
/* Uncoment one of SYS_CLOCK_MX */
#define SYS_CLOCK_MX RCC_CFGR_SW_PLL // System clock mux frof PLL
//#define SYS_CLOCK_MX RCC_CFGR_SW_HSE // System clock mux frof HSE
//#define SYS_CLOCK_MX RCC_CFGR_SW_HSI // System clock mux frof HSI
/* Предделитель с кварца работает только если задан EXT_CRYSTAL_EN */
#define PRE_EXT_ENTR_TO_PLL RCC_CFGR2_PREDIV_DIV1 // USE _DIV1 to _DIV16
/* Multiple PLL for stm32f030 and F070 can be high 48 Mhz*/
#define PRE_MULTIPLE_PLL RCC_CFGR_PLLMUL12 // from _PLLMUL2 to _PLLMUL2 PLL input clock*2
/* AHB Prescailer */
/* FLASH-память может работать на максимальной частоте 24 МГц
000: Zero wait state, if SYSCLK ? 24 MHz
001: One wait state, if 24 MHz < SYSCLK ? 48 MHz
*/
#define LATENCY_FLASH (0x1U) // from 0 to 1
//AHB prescaler
#define NO_PRESC 0// - без делителя // uncomment for no prescailer for AHB
#define PRE_AHB_KOF PRE_KOF_AHB_1 //HAL-AHB
#define PRE_KOF_AHB_1 RCC_CFGR_HPRE_DIV1 /*!< SYSCLK not divided */
#define PRE_KOF_AHB_2 RCC_CFGR_HPRE_DIV2 /*!< SYSCLK divided by 2 */
#define PRE_KOF_AHB_4 RCC_CFGR_HPRE_DIV4 /*!< SYSCLK divided by 4 */
#define PRE_KOF_AHB_8 RCC_CFGR_HPRE_DIV8 /*!< SYSCLK divided by 8 */
#define PRE_KOF_AHB_16 RCC_CFGR_HPRE_DIV16 /*!< SYSCLK divided by 16 */
#define PRE_KOF_AHB_64 RCC_CFGR_HPRE_DIV64 /*!< SYSCLK divided by 64 */
#define PRE_KOF_AHB_128 RCC_CFGR_HPRE_DIV128 /*!< SYSCLK divided by 128 */
#define PRE_KOF_AHB_256 RCC_CFGR_HPRE_DIV256 /*!< SYSCLK divided by 256 */
#define PRE_KOF_AHB_512 RCC_CFGR_HPRE_DIV512 /*!< SYSCLK divided by 512 */
// APB1 APB1
#define PRE_APB1_KOF PRE_KOF_APB_1 //APB1
#define PRE_KOF_APB_1 RCC_CFGR_PPRE_DIV1
#define PRE_KOF_APB_2 RCC_CFGR_PPRE_DIV2
#define PRE_KOF_APB_4 RCC_CFGR_PPRE_DIV4
#define PRE_KOF_APB_8 RCC_CFGR_PPRE_DIV8
#define PRE_KOF_APB_16 RCC_CFGR_PPRE_DIV16
/* This setting change MHz to I2C & USART */
// I2C CLOCK MUX Please comment one of this option
#define RCC_I2C1SW RCC_CFGR3_I2C1SW_HSI /*!< HSI oscillator clock used as I2C1 clock source */
//#define RCC_I2C1SW RCC_CFGR3_I2C1SW_SYSCLK /*!< System clock selected as I2C1 clock source */
// USART CLOCK MUX Please uncoment one of this
#define RCC_USARTSW RCC_CFGR3_USART1SW_PCLK /*!< PCLK clock used as USART1 clock source */
//#define RCC_USARTSW RCC_CFGR3_USART1SW_SYSCLK /*!< System clock selected as USART1 clock source */
//#define RCC_USARTSW RCC_CFGR3_USART1SW_LSE /*!< LSE oscillator clock used as USART1 clock source */
//#define RCC_USARTSW RCC_CFGR3_USART1SW_HSI /*!< HSI oscillator clock used as USART1 clock source */
uint8_t ClockInit(void);
#endif |
|
clock_cfg.c
| C | 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
| #include "clock_cfg.h"
//Настраиваем тактирование системы от внешнего кварца
//через PLL на максимально возможных частотах.
//Внешний кварц должен быть на 8МГц
//Возвращает:
// 0 - завершено успешно
// 1 - не запустился кварцевый генератор
// 2 - не запустился PLL
uint8_t ClockInit(void)
{
__IO int StartUpCounter;
////////////////////////////////////////////////////////////
//Запускаем кварцевый генератор
////////////////////////////////////////////////////////////
#ifdef EXT_CRYSTAL_EN
RCC->CR |= (1<<RCC_CR_HSEON_Pos); //Запускаем генератор HSE
//Ждем успешного запуска или окончания тайм-аута
for(StartUpCounter=0; ; StartUpCounter++)
{
//Если успешно запустилось, то
//выходим из цикла
if(RCC->CR & (1<<RCC_CR_HSERDY_Pos))
break;
//Если не запустилось, то
//отключаем все, что включили
//и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Останавливаем HSE
return 1;
}
}
#else
// run from internal high-speed OSC
RCC->CR |= RCC_CR_HSION; // run HSI
for(StartUpCounter=0; ; StartUpCounter++)
{
//Если успешно запустилось, то
//выходим из цикла
if(RCC->CR & (1<<RCC_CR_HSIRDY_Pos))
break;
//Если не запустилось, то
//отключаем все, что включили
//и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSION_Pos); //Останавливаем HSE
return 1;
}
}
#endif
////////////////////////////////////////////////////////////
//Настраиваем и запускаем PLL
////////////////////////////////////////////////////////////
#ifdef EXT_CRYSTAL_EN
// от внешнего кварца тактирования предделитель на ПЛЛ может быть разным
RCC->CFGR |= (RCC_CFGR_PLLSRC_HSE_PREDIV << RCC_CFGR_PLLSRC_Pos);
// выбираем предделитель
RCC->CFGR2 |= (RCC_CFGR2_PREDIV_DIV1 << RCC_CFGR2_PREDIV_Pos);
#else
// Если работаем от внутренего тактирования предделитель только 2
RCC->CFGR |= (RCC_CFGR_PLLSRC_HSI_DIV2 << RCC_CFGR_PLLSRC_Pos);
#endif
RCC->CFGR |= PRE_MULTIPLE_PLL;
// Prescailer AHB
RCC->CFGR |= (PRE_AHB_KOF << RCC_CFGR_HPRE_Pos);
// Prescailer APB1
RCC->CFGR |= (PRE_APB1_KOF << RCC_CFGR_PPRE_Pos);
RCC->APB2ENR |= (RCC_APB2ENR_SYSCFGCOMPEN);
// USART
RCC->CFGR3 |= (RCC_USARTSW << RCC_CFGR3_USART1SW_Pos);
RCC->CR |= (1<<RCC_CR_PLLON_Pos); //Запускаем PLL
//Ждем успешного запуска или окончания тайм-аута
for(StartUpCounter=0; ; StartUpCounter++)
{
//Если успешно запустилось, то
//выходим из цикла
if(RCC->CR & (1<<RCC_CR_PLLRDY_Pos))
break;
//Если по каким-то причинам не запустился PLL, то
//отключаем все, что включили
//и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Останавливаем HSE
RCC->CR &= ~(1<<RCC_CR_PLLON_Pos); //Останавливаем PLL
return 2;
}
}
////////////////////////////////////////////////////////////
//Настраиваем FLASH и делители
////////////////////////////////////////////////////////////
//Устанавливаем 2 цикла ожидания для Flash
//так как частота ядра у нас будет 48 MHz < SYSCLK <= 72 MHz
FLASH->ACR |= (LATENCY_FLASH<<FLASH_ACR_LATENCY_Pos);
RCC->CFGR |= (SYS_CLOCK_MX << RCC_CFGR_SW_Pos);//Переключаемся на работу от PLL
//Ждем, пока переключимся
while((RCC->CFGR & RCC_CFGR_SWS_Msk) != (0x02<<RCC_CFGR_SWS_Pos))
{
}
//После того, как переключились на
//внешний источник такирования
//отключаем внутренний RC-генератор
//для экономии энергии
#ifdef EXT_CRYSTAL_EN
RCC->CR &= ~(1<<RCC_CR_HSION_Pos);
#endif
//Настройка и переклбючение сисемы
//на внешний кварцевый генератор
//и PLL запершилось успехом.
//Выходим
return 0;
} |
|
main
| C | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| #include "stm32f0xx.h"
#include "I2C.h"
#include "ssd1306.h"
#include "clock_cfg.h"
void ssd1306_WriteCommand(uint8_t command)
{
I2C_Write_Transaction(SSD1306_I2C_ADDR,0x00,&command,1);
}
int main(void){
ClockInit();
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
GPIOA->MODER |= GPIO_MODER_MODER7_0;
i2c_Init2();
// моргнули светодиодиком, он моргает это так для успокоения что проц не висит
GPIOA->BSRR = GPIO_BSRR_BR_7;
for(int i = 0; i < 1200000; i++);
GPIOA->BSRR = GPIO_BSRR_BS_7;
while(1){
ssd1306_WriteCommand(0xAE);
}
} |
|
Сильно ногами не бейте, spi шину я победил, но тут руки уже опускаются.
0
|