|
STM32. eeprom at24c04b. I2C. DMA
Data-sheet atmel 24c04 DOC001094285
Весь исходный код. ide CooCox.
Компоненты:
stm32VLDISCOVERY(stm32F100RBT6)
eeprom AT24C04B-PU 4Kbit / DIP-8 / Atmel / I2C
2 подтягивающих резистора для линий SDA и SCL. 4.7-5.7 Ком (не просчитывал.)
(резистор 12 Ком для кнопки и кнопка (соединение pull down))
Соединение:
(eeprom ▶ mcu)
A0 ▶ не соединён
A1 ▶ Vcc mcu
A2 ▶ GND mcu
SDA ▶ PortB pin 7
SCL ▶ PortB pin 6
WP ▶ GND mcu (обычное выполнение операций чтения/записи)
GND ▶ GND mcu
Vcc ▶ Vcc mcu (+3.3; +5 v)
Ещё:
I2C настраивается ( i2c_dma.c ) по частоте шины APB1 = 24 МГц (регистры CR2, CCR, TRISE). При другой частоте нужно перенастроить.
Массив данных, который нужно записать в eeprom, разбивается на порции до 16 байт для постраничной записи при операции записи ( at24c04b.с ). После отправки каждой порции данных делается пауза до готовности eeprom получать следующую порцию данных. По даташиту операция записи может длиться до 5 миллисекунд. Таймер срабатывает через 1мс и делается проверка готовности. Если НЕ готово, то пауза в 1 мс повторяется.
некоторые модули:
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 | /* main.c*/
#include <stm32f10x.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>
#include "at24c04b.h"
#define btn1 GPIO_Pin_1
#define bufLen 33
void otherInit(void);
void Delay(uint32_t nTime);
uint8_t debounce(uint8_t last);
uint8_t dataBuff[bufLen] = {0, 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};
uint8_t * userData = dataBuff+1;
uint8_t rBuff[512];
int main(void)
{
uint8_t currentButton;
uint8_t lastButton;
//конкретное устройство
struct at24c04b_deviceInfo EEPROM_1;
EEPROM_1.I2Cx = I2C1;
EEPROM_1.devAddress = devN2;
otherInit(); // Initialize user button
lastButton = GPIO_ReadInputDataBit(GPIOA, btn1);
currentButton = lastButton;
// Configure SysTick Timer
if (SysTick_Config(SystemCoreClock / 1000))
while (1);
at24c04b_init(1);
while(1){
currentButton = debounce(lastButton);
if ( lastButton == Bit_RESET && currentButton == Bit_SET ){ //нажатие кнопки
at24c04b_Write(&EEPROM_1, userData, 32, 0);
at24c04b_Write(&EEPROM_1, userData, 32, 128);
at24c04b_Write(&EEPROM_1, userData, 32, 256);
at24c04b_Write(&EEPROM_1, userData, 32, 384);
at24c04b_Write(&EEPROM_1, userData, 32, 480);
at24c04b_Read(&EEPROM_1, rBuff, 512, 0);
//остановить под отладкой и проверить корректность
//(и посмотреть через анализатор)
at24c04b_Read(&EEPROM_1, rBuff, 16, 128);
}
lastButton = currentButton;
}
}
void otherInit(void){
GPIO_InitTypeDef GPIO_InitStructure;
// user button A0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
GPIO_InitStructure.GPIO_Pin = btn1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
static __IO uint32_t TimingDelay;
void Delay(uint32_t nTime){
TimingDelay = nTime;
while(TimingDelay != 0);
}
void SysTick_Handler(void){
if (TimingDelay != 0x00)
TimingDelay--;
}
uint8_t debounce(uint8_t last){
uint8_t current = GPIO_ReadInputDataBit(GPIOA, btn1);
if ( last != current ){
Delay(5);
current = GPIO_ReadInputDataBit(GPIOA, btn1);
}
return current;
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
/* Infinite loop */
/* Use GDB to find out why we're here */
while (1);
}
#endif
|
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 | /* at24c04b.h */
#ifndef AT24C04B_H_
#define AT24C04B_H_
//Пользовательская настройка-----------------------------------------------------------
// При использовании функции at24c04b_Write() для записи данных в неё передаётся
// указатель на массив данных.
// ! Есть нюансы при использовании DMA:
// 1. При работе с этим массивом данных нужно оставить нулевой байт пустым.
// В функцию передавать указатель на начало полезных данных.
// Пример:
// uint8_t dataBuff[20];
// uint8_t * userData = dataBuff + 1;
// userData передаётся в функцию.
// Это не касается функции at24c04b_Read() .
// 2. Второй нюанс:
// Так как адрес смещения (с которого будем что-то записывать) это тоже данные, то
// его нужно помещать в массив данных перед самими полезными данными( Делает это сама
// функция, а не пользователь). Для этого и нужен
// тот нулевой байт из первого нюанса. Но это работает для первой порции данных. А далее
// перед второй порцией данных (следующей шестнадцатибайтной страницей) нужно снова
// устанавливать байт смещения и т.д. Но чтобы это сделать нужно затереть байт в исходном
// пользовательском массиве данных. Что я и делаю. НО я сохраняю и восстанавливаю его. То есть
// после каждого цикла записи массив - в исходном состоянии. Использовал этот вариант для
// минимизации ненужных копирований.
#define I2CwithDMA (uint8_t) 1 // значения 0 (без DMA) или 1 (с DMA)
//Конец пользовательской настройки---------------------------------------------------
#include <stm32f10x.h>
// информация об одном устройстве
struct at24c04b_deviceInfo{
I2C_TypeDef* I2Cx;
uint8_t devAddress; // одна из констант devN1...devN4
};
// байт адреса "1010 AA P R/W"
// пользователь указывает только биты AA (константы devN1...devN4)
//все возможные адреса eeprom at24c04b на одной шине
#define devN1 (uint8_t) 0x0; //xxxx00xx
#define devN2 (uint8_t) 0x4; //xxxx01xx
#define devN3 (uint8_t) 0x8; //xxxx10xx
#define devN4 (uint8_t) 0x12; //xxxx11xx
//постоянная часть байта адреса для всех устройств
// (рисунок 9 в даташите)
#define DEVaddressConst 0xA0
//пятый ( старший) бит номера страницы
//он передаётся в первом(от 0) бите Адреса устройства
#define fifthPageBit 2
// Инициализация интерфейсов I2C.
//значения whichI2C:
// 1 - для инициализации шины I2C1
// 2 - I2C2
// 3 - инициализация обеих шин.
// Также инициализируется таймер(ожидание готовности устройства после записи)
void at24c04b_init(uint8_t whichI2C);
//Записывает переданный массив данных в EEPROM
//Записывает массив данных постранично с ожиданием готовности EEPROM к следующей порции данных.
//devInf - информация об устройстве
//buf - указатель на массив данных (там только данные, без каких-либо служебных байтов)
//nbyte - сколько нужно записать
//offset - смещение(адрес), с которого нужно записывать ( от 0 до 511)
// (всё, что выше 512 байт не будет записано.)
void at24c04b_Write(struct at24c04b_deviceInfo * devInf, uint8_t * buf, uint32_t nbyte, uint16_t offset);
//Читает данные из EEPROM в память.
//
//devInf - информация об устройстве
//buf - куда записывать данные
//nbyte - сколько нужно прочитать
//offset - смещение(адрес), с которого начнётся чтение ( от 0 до 511)
//
// (Когда в процессе чтения внутренний счетчик смещения дойдёт до конца (511 байт), то он сбросится в 0
// и чтение продолжится.
// например, запрашивает чтение 4 байт со смещения 510. Будут прочитаны 2 байта (до конца памяти) и
// 2 байта со смещения 0.)
void at24c04b_Read(struct at24c04b_deviceInfo * devInf, uint8_t * buf, uint32_t nbyte, uint16_t offset);
//делает паузу (1 миллисекунда) и проверяет, доступно ли указанное устройство для
// последующих операций(записи)
// возврат: 1 - устройство доступно, 0 - ещё не доступно.
uint8_t at24c04b_Test(struct at24c04b_deviceInfo * devInf);
// НЕ актуально.
//возвращает значение внутреннего счетчика адреса (меняется при каждой операции чтения или записи; это
// "последний" адрес, с которым была работа в последний раз + 1)
//Есть нюанс со старшим (9 битом) битом адреса...
// возвращает один байт.
//
// Фактически EEPROM всегда возвращает значение "255". Наверное, операция устарела.
// (даже если бы функция работала, то её сложно было бы применить)
// То есть эту фунцкию нет смысла использовать.
void at24c04b_CurrentAddressRead(struct at24c04b_deviceInfo * devInf, uint8_t * buf);
#endif
|
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 | /* i2c_dma.h */
//#include <stm32f10x_i2c.h>
#ifndef I2C_H_
#define I2C_H_
#include <stm32f10x.h>
//первичная настройка указанного интерфейса i2c
// и DMA, если он нужен.
// можно последовательно вызвать эту функцию для инициализации сразу обоих интерфейсов I2C
void i2c_lowLevel_init(I2C_TypeDef* I2Cx, uint8_t withDMA);
// Передаёт байты через i2c интерфейс.
// buf - указатель на передаваемый массив
// nbyte - количество
// SlaveAddress - "адрес" устройства (7-бит)
// offset - "смещение" (байт следующий за адресом устройства :)
void i2c_write(I2C_TypeDef* I2Cx, const uint8_t * buf, uint32_t nbyte, uint8_t SlaveAddress, uint16_t offset);
// Получает байты через i2c интерфейс.
// buf - указатель на массив, в который будут читаться данные.
// nbyte - количество
// SlaveAddress - "адрес" устройства (7-бит)
// offset - "смещение" (байт следующий за адресом устройства :)
// CAR - Current Address Read:
// 0 - обычное чтение (вначале Пустая запись для установки адреса и потом Чтение)
// 1 - чтение без пустой записи. Читается только один байт независимо от nbyte. (можно использовать для получения
// значения внутреннего счетчика адреса EEPROM)
void i2c_read(I2C_TypeDef* I2Cx, uint8_t * buf, uint32_t nbyte, uint8_t SlaveAddress, uint16_t offset, uint8_t CAR);
// аналогично i2c_write()
void i2c_dma_write(I2C_TypeDef* I2Cx, const uint8_t * buf, uint32_t nbyte, uint8_t SlaveAddress);
// аналогично i2c_read()
void i2c_dma_read(I2C_TypeDef* I2Cx, uint8_t * buf, uint32_t nbyte, uint8_t SlaveAddress, uint16_t offset);
//проверка доступности устройства (для последующей записи)
//
// возврат: 1 - устройство доступно,
// 0 - не доступно (например, идёт процесс записи и устройство не пока не отвечает)
uint8_t i2c_test(I2C_TypeDef* I2Cx, uint8_t SlaveAddress);
#endif
|
60
|
|
Комментарии