|
решение задачи 6.2 по учебнику Discovering the STM32 Microcontroller by Geoffrey Brown
STM32. Запись и чтение из EEPROM 25LC160. Интерфейс SPI. DMA
При нажатии на синюю кнопку пропускаем время дребезга контакта и начинаем процесс записи и чтения. Вначале тестируем отклики модуля памяти на команды. Записываем. Читаем.
Data-sheet 25AA160/25LC160/25C160
Весь исходный код. ide CooCox.
Компоненты:
STM32VLDISCOVERY (stm32F100RBT6)
EEPROM 25LC160/SN (16 Кбит, 2К x 8бит, Serial SPI, 2 МГц, SOIC, 8 вывод)
Соединение: ( см документацию на eeprom. TABLE 2-1)
CS ▶ PC10
SO ▶ PB14
WP ▶ +3.3V
VSS ▶ ground
SI ▶ PB15
SCK ▶ PB13
HOLD ▶ +3.3V
Vcc ▶ +3.3V
Недостатки реализации:
1. Расчёт на работающее устройство. Если произойдёт отказ модуля памяти, то при выполнении операций с ним выполнение кода зависнет в бесконечном цикле.
2. После настройки и включения SPI дальше я его не выключаю. В RM0041 в главе Serial peripheral interface (SPI) на стр 568 есть раздел 21.3.8 Disabling the SPI и там сказано, что когда передача завершена, то МОЖНО отключить SPI (даны инструкции)... и SPE=0 + отключить тактирование SPI.
Ещё:
DMA для SPI задействуется при передаче/получении больше 4 порций данных (8 или 16 бит).
Решение о включении DMA для SPI принимается в eeprom.c, а не в spi.c.
некоторые модули:
/* main.c */
#include <stm32f10x.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>
#include <stm32f10x_spi.h>
#include "eeprom.h"
#include <string.h>
// для примера 3
//static uint16_t INbuff[10] = {0xFFF1,0xFFF2,0xFFF3,0xFFF4,0xFFF5,0xFFF6,0xFFF7,0xFFF8,0xFFF9,0xFFFA};
//static uint16_t OUTbuff[10] = {0};
void otherInit(void);
void Delay(uint32_t nTime);
uint8_t debounce(uint8_t last);
#define rlen 100
int main(void)
{
uint8_t currentButton;
uint8_t lastButton;
uint8_t len;
uint8_t rbuf[rlen], rs;
//char mes[] = "The Read Status Register (RDSR) instruction provides access to the Status register.";
char mes[] = "Love is the only force capable of transforming an enemy into a friend. Martin Luther King, Jr.";
lastButton = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
currentButton = lastButton;
otherInit(); // Initialize user button
eepromInit();
// Configure SysTick Timer
if (SysTick_Config(SystemCoreClock / 1000))
while (1);
while (1) {
currentButton = debounce(lastButton);
if ( lastButton == Bit_RESET && currentButton == Bit_SET ){
// 1. Тест функций.
//
rs = eepromReadStatus(); //чтение регистра статуса
//активация функций записи в модуле памяти перед записью
eepromWriteEnable();
while (WIP(rs = eepromReadStatus()));
//проверка успешного выполнения предыдущей функции по флагу WEL
if (WEL(rs = eepromReadStatus()))
eepromWriteDisable(); // деактивация функций записи
else
assert_failed(__FILE__, __LINE__);
while (WIP(rs = eepromReadStatus()));
//проверка успешности выполнения функции eepromWriteDisable()
if (!WEL((rs = eepromReadStatus()))){
// Установка битов BP0 and BP1.
eepromWriteStatus( (uint8_t)4 | 8); // BP0 | BP1
} else
assert_failed(__FILE__, __LINE__);
while (WIP(rs = eepromReadStatus())); // ожидание окончания записи
// BP0 и BP1 установлены. Значение регистра статуса - 00001100.
if ((rs & ( 4 | 8))){ // проверяем, так ли это?
eepromWriteStatus(0x00); // обнуляем биты BP0 и BP1
}else
assert_failed(__FILE__, __LINE__);
//проверка установки бита WPEN
eepromWriteStatus( (uint8_t)0x80); // 10000000
// сброс бита WPEN
eepromWriteStatus(0x00);
while (WIP(rs = eepromReadStatus()));
rs = eepromReadStatus();
//test. end
// 2. Запись и чтение
len = (uint8_t)strlen(mes);
eepromWrite(mes, len, 4 ); //записать с адреса 4
eepromRead(rbuf, rlen, 0 ); // прочитать с адреса x00 100 байт
// 3. копирование данных из памяти в память через DMA.
// раскомментировать переменные INbuff и OUTbuff
//xchng_datablock(0, 1, OUTbuff, INbuff, 10);
}
lastButton = currentButton;
}//while
return 0;
}
void otherInit(void){
GPIO_InitTypeDef GPIO_InitStructure;
// user button A0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
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, GPIO_Pin_0);
if ( last != current ){
Delay(5);
current = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
}
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
/* eeprom.h */
#ifndef _EEPROM_
#define _EEPROM_
#include <stm32f10x.h>
#include <stm32f10x_gpio.h>
// настройка для линии ChipSelect
#define EEPROM_PORT GPIOC
#define EEPROM_CS GPIO_Pin_10
#define WIP(x) ((x) & 1) // бит "процесс записи не закончен."
#define WEL(x) ((x) & 2) // бит "включены ли операции записи"
//команды
enum eepromCMD {
cmdREAD = 0x03, //читать данные из памяти
cmdWRITE = 0x02, //записывать данные в память
cmdWREN = 0x06, //включение операций записи
cmdWRDI = 0x04, //отключение операций записи
cmdRDSR = 0x05, //читать регистр статуса
cmdWRSR = 0x01 //записать в регистр статуса
};
// Инициализация.
// Настройка SPI2 c программной линией ChipSelect, включение тактирования для DMA1
void eepromInit(void);
//в этом модуле памяти есть регистр статуса. Его биты:
// 0 - WIP // запись в процессе выполнения
// 1 - WEL // "защёлка" включения записи.
// 2 - BPO // защита блока памяти от записи
// 3 - BP1 // защита блока памяти от записи
// 4 - -
// 5 - -
// 6 - -
// 7 - WPEN // защита от перезаписи энергонезависимых битов в регистре статуса
//WIP, WEL - только чтение
//BPO, BP1, WPEN - чтение и запись, Энергонезависимые.
//Перед каждой операцией записи (данных в модуль памяти или записи в
//регистр статуса) нужно устанавливать бит WEL в регистре статуса. Деляется это
// отправкой команды WREN.
// Этот бит сбрасывается самим модулем памяти в случае успешного выполнения
// команд: WRDI, WRSR, WRITE.
// (вызывать эту функцию перед вызовом функций eepromWriteStatus() и eepromWrite()
// не нужно - вызов используется внутри них)
void eepromWriteEnable(void);
//то же, что и eepromWriteEnable(), только наоборот (выключение).
void eepromWriteDisable(void);
//прочитать регистр статуса
// возврат: значение регистра статуса
uint8_t eepromReadStatus(void);
//отправка команды WRSR.
//записать в регистр статуса новые значения битов BP0 и BP1 для выбора одного
//из четырёх "уровней" защиты массива памяти.
//"уровни":
// _BP1__BP0
// 1. 0 0 - нет защиты
// 2. 0 1 - защита верхней 1/4 памяти ( 0600h - 07FFh)
// 3. 1 0 - защита верхней 1/2 памяти ( 0400h - 07FFh)
// 4. 1 1 - защита всей памяти ( 0000h - 07FFh)
void eepromWriteStatus(uint8_t status);
//Запись данных в модуль памяти.
// buf - указатель на данные
// cnt - количество байт
// offset - начальный адрес в массиве памяти
int eepromWrite(uint8_t *buf, uint8_t cnt, uint16_t offset);
//Чтение данных из модуля памяти.
// buf - куда читать
// cnt - количество байт
// offset - начальный адрес в массиве памяти
int eepromRead(uint8_t *buf, uint8_t cnt, uint16_t offset);
// в dataSheet 25AA160/25LC160/25C160 в таблице
// TABLE 3-3: WRITE-PROTECT FUNCTIONALITY MATRIX
// приведены все сочетания битов WPEN и WEL и пина WP для выбора
// различных возможностей защиты от записи.
// (в моём случае пин WP был всегда соединён с "+".)
#endif
/* spi.h */
#ifndef SPI_H_
#define SPI_H_
enum spiSpeed { SPI_SLOW, SPI_MEDIUM, SPI_FAST };
// Основная настройка SPI. Работает только с SPI2.
void spiInit(SPI_TypeDef* SPIx);
// Передача/получение данных по 8 бит
// SPIx - только SPI2
// rbuf - указатель на буфер, куда будут загружены данные
// tbuf - указатель на отправляемые данные
// cnt - количество (по 8 бит)
//
// Если в tbuf передан указатель, а в rbuf - 0, то будут отправлены данные, а получаемые
// данные никуда записываться не будут.
// Если наоборот - будут получены данные, отправляться будет "пустой" сигнал (0xff).
// Если оба указателя не нулевые, то данные будут переданы и получены.
int spiReadWrite(SPI_TypeDef* SPIx, uint8_t * rbuf, const uint8_t * tbuf, int cnt, enum spiSpeed speed);
// Передача/получение данных по 16 бит
// см. описание функции spiReadWrite() (8 бит -> 16 бит)
int spiReadWrite16(SPI_TypeDef* SPIx, uint16_t * rbuf, const uint16_t * tbuf, int cnt, enum spiSpeed speed);
#endif
/* spi_dma.h */
#ifndef SPI_DMA_H_
#define SPI_DMA_H_
#include <stm32f10x.h>
// Инициализация. Включение тактирования
void spi_dma_Init(void);
//Передача/Получение данных по SPI c DMA
//(основные настройки SPI должны быть уже выполнены (spi.h.c) )
// SPIx - SPI1 или SPI2
// half: 0 - 8 бит, 1 - 16 бит
// tbuf - указатель на отправляемые данные
// rbuf - указатель на буфер, куда будут загружены данные
// count - количество порций данных размером по параметру half
//
// Если в tbuf передан указатель, а в rbuf - 0, то будет запущена фукнция отправки.
// Если наоборот - будет запущена функция получения.
// Если оба указателя не нулевые, то будет запущено копирование из памяти в память
int xchng_datablock(SPI_TypeDef *SPIx , uint8_t half , const void *tbuf , void *rbuf , unsigned count);
#endif
|
|
Комментарии
А, где все *.c файлы?