Jump to content

    
Sign in to follow this  
ILSF15

Запись и чтение Flash MSP430G2553 - помогите дилетанту

Recommended Posts

Здравствуйте. Извиняюсь, если это слишком глупая просьба, но я буквально дилетант - чуть-чуть поднахватался Си и, собственно, понимания, как работает микроконтроллер.

Мне всего-то и надо: иметь возможность до выключения контроллера сравнить значение единственной переменной unsigned char, (это, наверно, байт?) с записанным на флэш, если изменилось - записать новое, а когда питание вернётся - присвоить этой переменной сохранённое значение.

И я просто тону в информации, т.к. люди тут по теме публикуют солидные куски кода с продвинутыми задачами, в которых я теряюсь и не могу понять, как, собственно, достигнуть моей, казалось бы, несложной цели. И просить-то неудобно из-за этого, но я уже, наверно, пару дней в общей сложности убил - дилетант, что поделать...

Насколько я понял - важно, чтобы в момент записи были выключены прерывания. В связи с этим вопрос: учитывая, что кусок кода перед выключением вызывается прерыванием по вектору PORT2 (у меня вообще в программке вся работа по прерыванию PORT1 и только перед выключением PORT2) - нужно ли пересматривать из-за этого принцип действия программы так, чтобы перед записью она выходила в main, и если да - как указать ей, куда именно выходить, учитывая, что сейчас у меня в main только инициализация портов и переменных, и заканчивается всё уходом в LPM4?

Подумал, что может быть важно: контроллер LaunchPad MSP-EXP430G2 rev1.5, чип M430G2553

Edited by ILSF15

Share this post


Link to post
Share on other sites
8 hours ago, ILSF15 said:

. . .  чтобы перед записью она выходила в main, и если да - как указать ей, куда именно выходить, учитывая, что сейчас у меня в main только инициализация портов и переменных, и заканчивается всё уходом в LPM4?

примерно так

main()
{
int a, b, c . . . .  = 0 
int flag = 0;
   while(1)
  {
    LPM;
    if( flag == 1 ) 
     {    . . . . . 
      }
    else 
    if( flag == 2 ) 
     {    . . . . . 
      }
     
     flag = 0;
  }//w
}

#pragma vector=PORT1_VECTOR
__interrupt void PORT1_ISR(void)
{
  flag = 1;
  __no_operation();
}

если питание процессора "не гарантировано" или не соответствует допустимому, писать во флеш нельзя.

Share this post


Link to post
Share on other sites
1 hour ago, jcxz said:

Не обманывайте начинающего.

Во, уже есть интрига :)  ружжо повешено на стену :biggrin: 

В смысле что у ТС LPM4 ? так я его и не указывал.

Share this post


Link to post
Share on other sites

Я не специалисть в Си, но, возможно, вам помогут несколько замечаний.
Цитаты приведены из документа "http://www.ti.com/lit/ug/slau144j/slau144j.pdf?ts=1590312294147". Для понимания работы флэш памяти нужно бы прочитать как минимум раздел 7.3.

 

1. Вы не можете просто так изменять биты во встроенной флэш памяти. Это касается любого сегмента памяти.
Цитата раздела 7.2.3:
"The erased level of a flash memory bit is 1. Each bit can be programmed from 1 to 0 individually but to reprogram from 0 to 1 requires an erase cycle."
Т.е. если при записи нового байта хоть один бит должен быть изменен с "0" на "1", то это не получится. Сначала нужно будет стереть сегмент целиком, чтобы все биты стали равны "1", и только после этого можно записать новый байт.

 

2. Запись или стирание можно выполнять из любого участка программного кода, в том числе из обработчика прерывания. Важно, чтобы процесс записи не был прерван каким-либо другим событием, т.е. срабатыванием Watchdog, сигналом сброса, прерыванием, или просадкой питания. Прерывания перед записью/стиранием можно просто запретить. Причем, насколько я понял, в контроллере M430G2553 прерывания запрещаются автоматически, т.е. об этом заботиться вообще не нужно.

 

3. В момент записи или стирания флэш памяти нужно гарантировать, чтобы не сработал таймер Watchdog. Для этого рекомендуют его отключать. На практике я часто настраивал Watchdog так, чтобы он гарантировано не сработал раньше окончания процесса записи или стирания.

Edited by Vas123

Share this post


Link to post
Share on other sites

k155la3ага, вроде понятно, спасибо

jcxz, ну, не настолько уж я полный ноль :)

Vas123по пунктам 2 и 3 вообще нет проблем, в программе Watchdog не используется вообще, остановка - первый оператор main, и прерывания друг друга не прерывают, остановлены, мне это как раз помеха. То, что надо стереть и потом записать - не проблема. По задумке, внешнее оборудование должно вызывать прерывание порта 2, а порт 2 - только после того, как всё отработает (включая запись, если изменилось состояние) давать внешнему оборудованию команду на обесточивание контроллера (причём в этот момент питание его - строго от общей со всей системой батареи). Т.е. потеря питания возможна только в очень экстренной ситуации (села батарея, например), причём в этой ситуации уже не так критично, что устройство забыло, где оно вообще было. А следующий нормальный цикл, как я это понимаю, просто сотрёт некачественные данные и запишет их заново. То есть, не так критично.

 

По первому пункту - это я понял, поэтому я и написал "байт", а не "бит", а в этот байт я без труда заранее закодирую то небольшое число возможных состояний на выходе, которое мне нужно. Причём, подчёркиваю: чаще всего вообще ничего писать не надо, т.к. под конец в норме вся система должна приходить в то же состояние, из которого выходила в начале, что значительно упрощает задачу и сберегает время жизни флэш. Но для редких случаев изменения режима очень желательно вместо "насильственного" возвращения "на исходную" иметь возможность рассказать контроллеру, что: переменная рас была 0/1 (в десятеричной форме char это может обозначаться как <=100/200), переменные два были от 0 до 5 - соответственно, в char первая обозначится десятками, вторая - единицами. В качестве минимального контроля ошибок записи - проверка, что запись не больше 200, десятки - не больше 50, единицы - не больше 5. В противном случае можно реализовать "насильственный" рестарт.

 

Но всё это всё ещё упирается в то, о чём я нижайше прошу присутствующих тут профи: одарить меня двумя кусками кода (желательно - которые можно было бы воткнуть прямо в то место в программе, где они должны выполняться, поскольку операции одноразовые), один из которых будет читать из флэш (думаю, раздел INFO вполне подойдёт, раз уж он для этого предназначен, а данных - минимум) значение одного байта и присваивать его переменной типа unsigned char по имени, скажем, fl, второй - стирать тот же участок флэш и записывать в него текущее значение той же переменной fl. Если можно. Пожалуйста. А то я, как я уже сказал, так и не понял принципы этих действий в той инфе, которую нашёл, а уж в командах ассемблера я вообще ни в зуб ногой (т.е. что-то когда-то смутно знал, но и то уже забыл...)

Edited by ILSF15

Share this post


Link to post
Share on other sites

Вот комплект софта под Вашу плату (там есть работа с записью флеша), может кто оперативно посмотрит и поделится сокральными знаниями.

Я с этим G2553 "не плавал", но вполне похоже на F5438.

ТС, какой компилятор используете ?

работа с флеш упоминается в файлах (софт для платы с MSP430F2553, архив внизу)

HAL_MSP-EXP430G2_Sharp128x128.c
Sharp128x128.c
HAL_MSP-EXP430G2_Sharp128x128.h
Sharp128x128.h

стирание-запись флеш (для MSP430F5438A)

// Инкремент счетчика загрузок
void FLASH_IncParCountRestarts(void)
{
	// Инициализация и стирание блока flash
	unsigned short *Flash_ptr;                          	// Flash pointer
	Flash_ptr = (unsigned short *)FLASH_ADDRESS_INFORB;      // Initialize Flash pointer
    __disable_interrupt();	

	// Сохранение предыдущих значений
	SSecurePar SPar;
	SPar.Par_CountChange = SecurePar.Par_CountChange;
	SPar.Par_CRC		 = SecurePar.Par_CRC;
	SPar.Par_CountRestarts = SecurePar.Par_CountRestarts;
	
	// Инициализация и стирание блока flash
	FCTL3 = FWKEY;                            // Clear Lock bit	
	FCTL1 = FWKEY + ERASE;                    // Set Erase bit
	*Flash_ptr = 0;                           // Dummy write to erase Flash segment
	FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
	
	// ------------------------------------
	// Запись
	SPar.Par_CountRestarts++;
	*Flash_ptr++ = SPar.Par_CountChange;
	*Flash_ptr++ = SPar.Par_CRC;
	*Flash_ptr   = SPar.Par_CountRestarts;
	// ------------------------------------
	
	// Завершение записи Flash
	FCTL1 = FWKEY;                            // Clear WRT bit
	FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
	
    __enable_interrupt();
}

 

MSP-EXP430G2ET_Software_Examples.zip

Share this post


Link to post
Share on other sites

Собственно, в личке уже ответил - IAR.

Flash_ptr обязан быть unsigned short? И всё ещё не могу из этого вычленить, где именно "извлекается" и передаётся переменной текущее значение из flash, и куда транслируется требующее записи значение. Слишком много переменных с не очень говорящими для меня именами... вот эту мою переменную fl типа unsigned char - её куда подставить, чтобы записать в неё содержимое записи на флэшке? И куда - чтобы значение переменной на флешку записать? То есть, я понимаю, что первое - куда-то в "сохранение", а второе - куда-то в "запись", но там по четыре строчки, как я понимаю, там по три куска данных? Что из них можно просто проигнорировать, а что - то, что надо?

И ещё - это записывается в Flash INFO 1000/1010 (или какие они там)? Не хотелось бы трогать не предназначенные по умолчанию для этого куски флэшки. 

Share this post


Link to post
Share on other sites

Вот я тут попытался осмыслить написанное выше, загнал в IAR вот такую программу:

#include "msp430g2553.h"

unsigned char m; volatile unsigned int ctr=0;
unsigned short *Flash_ptr = (unsigned short *)0x1000;

#define RLED_ON         P1OUT |= BIT0
#define RLED_OFF        P1OUT &= ~BIT0
#define GLED_ON         P1OUT |= BIT6
#define GLED_OFF        P1OUT &= ~BIT6
#define LEDS_INIT       P1DIR |= BIT0 + BIT6; RLED_OFF; GLED_OFF
#define DELAY           __delay_cycles(300000)
#define BT_INIT         P1DIR &= ~BIT3; P1REN |= BIT3;

void main() { WDTCTL = WDTPW + WDTHOLD; LEDS_INIT; BT_INIT; __disable_interrupt();

RLED_ON;
for (volatile long int c=100000; c>0; c--) { if (P1IN&BIT3); 
else { DELAY; m=37; 

	FCTL3 = FWKEY;                            // Clear Lock bit	
	FCTL1 = FWKEY + ERASE;                    // Set Erase bit
	*Flash_ptr = 0;                           // Dummy write to erase Flash segment
	FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
          
        *Flash_ptr   = m;  
          
        FCTL1 = FWKEY;                            // Clear WRT bit
	FCTL3 = FWKEY + LOCK;                     // Set LOCK bit

break; }}
  RLED_OFF; DELAY;
 
  
  if (m==37) { GLED_ON; DELAY; GLED_OFF; DELAY; }
  else { RLED_ON; DELAY; RLED_OFF; DELAY;}
  
}

IAR при компиляции говорит - нет ошибок. И что - это запишет m на флэш? А при следующем включении питания - она будет уже равна 37? Или я всё-таки что-то неправильно понимаю? (боюсь записывать на контроллер, чтобы чего-нибудь не напортачить)

Edited by ILSF15

Share this post


Link to post
Share on other sites
4 hours ago, ILSF15 said:

IAR при компиляции говорит - нет ошибок. И что - это запишет m на флэш? А при следующем включении питания - она будет уже равна 37? Или я всё-таки что-то неправильно понимаю? (боюсь записывать на контроллер, чтобы чего-нибудь не напортачить)

Через IAR/Debug работайте без опаски. Ну не было у меня случая, чтобы при этом "все пропало" и пошел дым.

Опасность только если будете "заливать" код через утилиту программирования и установите бит защиты.

Пробуйте выполнить программу в IAR/Debug.

На последнем if поставьте по F9 точку останова. После этого можно посмотреть область памяти, 

куда была выполнена (?) запись (физ. адреса 0x1000, 0x1001) 

unsigned short *Flash_ptr = (unsigned short *)0x1000;

 

ps Функция работы с флеш из архива  MSP-EXP430G2ET_Software_Examples.zip

"вызовы" вроде UnlockFlashMemory() - макросы

#define UnlockFlashMemory()                    	\
       FCTL3 = FWKEY
//! This function initializes the display buffer and discards any cached data.

void InitializeDisplayBuffer(void *pvDisplayData, uint8_t ucValue)
{
	uint16_t i=0,j=0;
	uint8_t *pucData = pvDisplayData;

	uint16_t ucSegment = 0;

	UnlockFlashMemory();

	for(i =0; i< LCD_VERTICAL_MAX; i++){
		for(j =0; j< (LCD_HORIZONTAL_MAX>>3); j++){
			if(ucSegment==0 && ClrBlack != ucValue){
				SetFlashAccessRight(ERASE);
				//Dummy write to erase Flash segment
				*pucData=0;

				SetFlashAccessRight(WRT);
			}
			while(FCTL3&BUSY);
			*pucData++ = ucValue;
			ucSegment++;
			if(ucSegment == 512)
				ucSegment = 0;
		}
	}

	FinishMemoryWrite();
}

 

 

Share this post


Link to post
Share on other sites

Методом научного тыка для присваивания m содержимого флэш перед последним if была добавлена операция 

m = *Flash_ptr;

После чего он стал успешно подмаргивать БЕЗ нажатия кнопки зелёным даже после того, как я безжалостно сменил unsigned char m на unsigned char m=0 и перезалил в контроллер.

Другими словами, успешно записал и считал то, что надо. Спасибо всем, отдельное и огромное - k155la3!!!

 

Подводя итог - и на случай, если кому-то ещё понадобится такой ответ, порядок действий таков:

а) исключить срабатывание прерываний во время процедуры записи;

б) объявить переменную *Flash_ptr (насколько я знаю, она должна быть глобальной):

unsigned short *Flash_ptr = (unsigned short *)0x1000;

в) выполнить процедуру стирания и установки в режим записи, код:

	FCTL3 = FWKEY;                            // Clear Lock bit	
	FCTL1 = FWKEY + ERASE;                    // Set Erase bit
	*Flash_ptr = 0;                           // Dummy write to erase Flash segment
	FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation

г) присвоить *Flash_ptr то значение, которое хотим записать

д) выключить запись, код:

        FCTL1 = FWKEY;                            // Clear WRT bit
	FCTL3 = FWKEY + LOCK;                     // Set LOCK bit

е) чтобы получить записанное на флэш значение, достаточно присвоить требуемой переменной упомянутую *Flash_ptr:

m = *Flash_ptr;

Это и даст искомый результат. Ещё раз всем спасибо.

Share this post


Link to post
Share on other sites

Кстати, надо ли ради одного байта заморачиваться проверкой/замедлением частоты процессора, которое вроде бы нередко упоминается в описании процедуры записи на флэш?

 

UPD. Применительно к стёртому второму вопросу: видимо, я фигнёй страдаю, судя по тому,что я из мануалов понял. Всё равно стирается целый сегмент из 64 байт, а в него я могу записать все три переменных, как есть, по два раза, плюс добавить целый байт на проверку записи, контрольные суммы, короче, не актуально. Куда актуальней разобраться в том, какие сегменты можно трогать, а какие не очень :)

Edited by ILSF15

Share this post


Link to post
Share on other sites
On 5/25/2020 at 5:04 AM, ILSF15 said:

 надо ли ради одного байта заморачиваться проверкой/замедлением частоты процессора, которое вроде бы нередко упоминается в описании процедуры записи на флэш?

надо (смотря от какой линии - ACLK/MCLK-SMCLK ), тк контроллер флеш тактируется от основной тактовой системы процессора, и в datasheet для этого узла установлено ограничение (порядка 200-400 кГц, подробнее надо смотреть даташит конкретного процессора). Если MCLK-SMCLK, и делитель в контроллере флеш не обеспечивает допустимую частоту, надо (на время операции записи) снизить частоту MCLK или переключить на другой источник тактирования.

Share this post


Link to post
Share on other sites

Этот комментарий в основном для неофитов, ищущих понимания, как и я. 

Во-первых, во избежание искажения данных, как перед стиранием, так и перед записью рекомендуется добавить проверку доступности флэш (важно при работе с флэш из RAM) и установку частоты (важно: частота, нужная для стирания/записи флэш и частота, задаваемая таймером, разнятся от процессора к процессору, проверяйте datasheet своего)

 

      while (FCTL3&BUSY) { ; }              // Check BUSY bit


      FCTL2 = FWKEY+FSSEL0+FN0;      // Set ACLK (примерно 320 kHz) для моего процессора. 

 

Во-вторых, с теми же целями, прямо после окончания стирания или записи байта/слова, рекомендуется по тем же причинам повторить проверку доступности:

      while (FCTL3&BUSY) { ; }

В-третьих, после некоторого анализа итог был несколько переосмыслен. Поскольку при команде на стирание стирается всё равно все 64 байта (весь сегмент), нерационально производить стирание каждый раз, когда надо записать новые данные, это впустую расходует ресурс флэш. Поэтому имеет смысл (я это сделал пока что "на бумаге", поэтому пока не стану выкладывать коды) заморочиться организацией новой записи в первый свободный байт/слово (*Flash_ptr&0xFF - условие, что все биты байта/слова = 1) с предварительным контролем, что оно не за пределами сегмента. Если за пределами - произвести стирание "своего" сегмента и запись в его начало.

Границы сегментов: D - 0x1000 - 0x103F, C - 0x1040 - 0x107F, B - 0x1080 - 0x10BF. Сегментом А пользоваться не рекомендуют все руководства, и он залочен отдельно, в нём, на сколько я понял, хранятся данные по "железу" (опять же может разниться в зависимости от процессора, проверяйте datasheet).

Т.е. стирание, например, сегмента D имеет смысл делать, только если первый свободный байт/слово находится по адресу 0x1040.

 

Кроме того, кмк, имеет смысл заморочиться перепроверкой успешности записи (простым сравнением записи с содержащейся в RAM переменной) и повторной записью в следующий адрес (опять же, с проверкой наличия свободного места и границ сегментов).

Чтение, соответственно, производить после поиска последней актуальной версии данных. Это можно организовать поиском последнего с конца сегмента пустого байта/слова или первого с конца непустого байта/слова. Поскольку я решил нулевой бит байта в данных использовать как "контрольный", поиск последней актуальной версии я организовал, как поиск первого с конца байта, выполняющего условие (*Flash_ptr&BIT0)==0.

Эти процедуры добавляют возни с обработкой, зато сберегают ресурс флэш и повышают шанс на то, что данные успешно сохранены.

Важно: поскольку стирание и запись получаются разнесены во времени, команды должны выглядеть так:

запись (подразумевается, что значение Flash_ptr уже установлено в адрес первого свободного байта)

      while (FCTL3&BUSY) { ; }       // Check BUSY bit

      FCTL2 = FWKEY+FSSEL1+FN0;      // Set SMCLK /2 (approx 500kHz)

      FCTL1 = FWKEY + WRT;           // Set WRT bit for write operation

      *Flash_ptr = data;             // Write your variable 

      while (FCTL3&BUSY) { ; }       // Check BUSY bit

      FCTL1 = FWKEY;                 // Clear WRT bit

      FCTL3 = FWKEY + LOCK;          // Set LOCK bit

стирание (подразумевается, что значение Flash_ptr уже установлено в адрес начала сегмента)

      while (FCTL3&BUSY) { ; }       // Check BUSY bit

      FCTL2 = FWKEY+FSSEL1+FN0;      // Set SMCLK /2

      FCTL3 = FWKEY;                 // Clear Lock bit

      FCTL1 = FWKEY + ERASE;         // Set Erase bit

      *Flash_ptr = 0;               // Dummy write to erase Flash segment

      while (FCTL3&BUSY) { ; }       // Check BUSY bit

      FCTL3 = FWKEY + LOCK;          // Set LOCK bit

P.S. Правильно ли я понимаю, что *Flash_ptr возвращает содержимое памяти по адресу, а Flash_ptr (без звёздочки) сам адрес? Если я прав (для таких, как я, дилетантов уточняю) - операции с адресными переменными должны производиться без звёздочки. Прямое присваивание или проверка конкретного адреса - в формате (unsigned <type> *)0x10XX, где type - тип переменной (если, опять же, не ошибаюсь, гуру поправят) - char должно соответствовать байту, а short - слову, а XX - нужное значение адреса в диапазоне нужного сегмента в шестнадцатеричном виде.

Edited by ILSF15

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this