+7(960) 250-82-68 spam@mirossa.ru


  MCU         C           1С         PHP       JAVA      разное 


Статьи
 
 

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)

connection STM32 and 25LC160

Ещё:
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

Комментарии




information