fbpx

Może się powtórzę, ale zestawy od ST są świetne. Do każdej płytki dostajemy w pełni funkcjonalny programator ST-Link. No prawie w pełni, ale w za to 100% pokrywający potrzeby montowanych w zestawy mikrokontrolera. Czy znasz wszystkie jego funkcjonalności? Mógłbym założyć się, że nie, ale zakładam też, że zaawansowani użytkownicy również to czytają, więc nie będę ryzykował 😉

Czym jest ST-Link

W skrócie jest to programator zgodny z MCU firmy ST. ST-Link default’owo jest integralną częścią zestawów Nucleo, lecz można je wyłamać i używać jako odrębnego tworu. Niestety Nucleo-32 jest wyjątkiem i nie oddzielimy fizycznie ST-Linka od części zasadniczej. Podobnie jest z płytami Discovery – też nie wyłuskamy sobie programatora. Posiadając kilka Nucleo mam zapas programatorów dla siebie i moich dzieci do końca życia.

Co może ten mały potworek? Wiele feature’ów opisanych zostało w dokumentacji dotyczącej  programatorów zintegrowanych z zestawami ewaluacyjnymi. Dodatkowo w dokumentacji ogólnej zestawów Nucleco jest co nie co opisane. Polecam Ci zapoznanie się z dokumentami.

TN1235 - Overview of the ST-LINK embedded in STM32 MCU Nucleo

STM32 Nucleo

Podchodząc poraz pierwszy do swojego egzemplarza wypadałoby zaktualizować oprogramowanie w programatorze. Zrobisz to za pomocą ST-Link Utility. Najnowszą wersję pobierzesz ze strony ST. Niestety trzeba mieć konto, aby coś od nich pobrać. Całą operację wykonuje się bardzo prosto. Uruchamiasz narzędzie i  podłączasz Nucleo lub inną płytkę z ST-Link’iem. Następnie z górnego menu wybierz ST-Link >> Firmware Update.

Po klinknięciu w Device Connect program powinien wykryć podłączony do komputera programator. Firmware Version to aktualne oprogramowanie, które siedzi w programatorze. Poniżej znajduje się najnowsza wersja, która jest dostępna. W moim wypadku jest taka sama jak w sprzęcie. Klikając Yes, uruchamiasz procedurę aktualizacji.

Programowanie

Jeśli przeczytałeś uważnie dokumentację to znasz już niektóre cechy ST-Linka. Jego najważniejszą i podstawową funkcją jest niewątpliwie programowanie mikrokontrolera. Podłączając Nucleo w całości lub inne płytki ze zintegrowanym ST-Linkiem, mamy możliwość wgrania swojego programu do MCU. Można zrobić to poprzez ST-Link Utility wskazując skompilowany plik binarny z firmware’m lub bezpośrednio w środowisku SW4STM32 które jest zalecane przez ST oraz z którego ja korzystam.

Fajnym “bajerem” jest programowanie poprzez wrzucenie binarki do pamięci masowej pod którą montuje się ST-Link w systemie operacyjnym. Sposób ten jest podyktowany założeniami projektu mbed. Wyobraź sobie, że jesteś w terenie u klienta, nie masz swojego zestawu oprogramowania bo siadł Ci komputer, ale ktoś podesłał Ci binarkę z poprawionym błędem. Dzięki temu, że programator montuje się jak pendrive, przerzucasz na niego binarkę z porzyczonego laptopa czy nawet z telefonu obsługującego OTG i… gotowe. Układ jest zaprogramowany. Świetne, co nie? Ja z tego sposobu korzystam baardzo rzadko, ale widzę w nim potencjał.

Wspominałem wcześniej o wyłamywaniu ST-Linka z płytek Nucleo. Po takim zabiegu, możemy programować dowolne układy podłączając się poprzez złącze CN4. Aby było to możliwe należy jeszcze ściągnąć zworki z CN2. Ściągając te zworki można również programować układy zewnętrzne bez wyłamywania ST-Linka z Nucleo. Złącze CN4 posiada standardowy pinout:

Uwaga. Pin 1 nie służy do zasilania programowanego układu! Pin ten podłączamy do zasilania programowanego MCU po to aby ST-Link dopasował się do niego. Dzięki temu możemy programować układy o bardzo niskich napięciach roboczych. Mimo tego, że nota ST-Linka dołączanego do zestawów Nucleo mówi, że mniej niż 3 V nie da rady – mi udało się z powodzeniem programować i debugować układy zasilane 1,8 V.

Pin 6 ja zawsze pomijam 😉

Debugowanie

Oprócz programowania mamy możliwość prawdziwego debugowania kodu. Pamiętam jak na AVR mordowałem się zawsze z debugową diodą, która była ustawiana gdzieś w kodzie jako pułapka. Miało to swój urok i lubię to do dzisiaj czasem robić. Problem pojawia się kiedy np. procesor chodzi, komunikacja wygląda na poprawną, ale nadal za nic w świecie program nie działa poprawnie. Diodą nie podglądniemy wartości zmiennych w RAMie.

Tak samo diodą nie zatrzymasz programu w dowolnym momencie. Natomiast z poziomu debugowania już tak. Postawisz tzw. breakpoint’y które zatrzymają wykonywanie programu kiedy MCU dojdzie do nich. Jest to niesamowicie potężne narzędzie do rozwiązywania problemów, a problemy to nawet 80% pisania kodu. Chyba, że ktoś preferuje metodę gumowej kaczuszki (link). Ja oczywiście swoją kaczkę na biurku mam 😉

Komunikacja UART

ST-Link jest jednocześnie konwerterem UART <=> USB. Tworzy on wirtualny port COM w systemie operacyjnym naszego komputera. Dzięki temu możemy printować co chcemy na terminalu naszego PC. Nie tylko printować, bo odbierać z PC również możemy. Świetna sprawa. Do tego celu wykorzystywany jest port UART2 znajdujący się na pinach PA2 i PA3.

Tak samo jak programowanie, używanie konwersji UART <=> USB jest możliwe na zewnątrz płytek ewaluacyjnych. Możliwe jest to za pomocą złącza CN3 do którego dostarczamy UART mikrokontrolera. Z tym, że nie ma wygodnej zworki do rozłączania jak w przypadku programowania. Aby użyć UART na zewnątrz bez wyłamywania ST-Linka należy wylutować zworki SB13 i SB14 znajdującej się na spodzie PCB.

Semihosting

Prawdopodobnie nie słyszałeś o tym. Ja sam dowiedziałem się o tym całkiem niedawno. Co jeśli chciałbyś coś printować do komputera, ale nie masz wolnego żadnego interfejsu UART lub USB bo wszystko jest zajęte przez układy peryferyjne w urządzeniu? Dupa, nie da się co nie? A jednak! Z pomocą przychodzi tzw. semihosting. Jest to ficzer polegający na printowaniu do gdb także używa się go jedynie w trybie debugowania. Niestety działa to tylko w jedną stronę, ale przy debugowaniu głównie tę właśnie stronę używamy – wysyłamy do PC. Kolejną wadą jest znaczne spowolnienie wykonywania programu przez MCU, dlatego trzeba wyłączać tą metodę w docelowym programie na mikrokontroler. Brak uruchomienia MCU w trybie debugowania również nie pozwoli na prawidłową pracę jeśli używany jest semihosting. Jak to skonfigurować i jak używać? Do dzieła!

Potrzeba jest w kodzie specjalna funkcja asemblerowa piszącza na interfejs ST-Link.

void send_command(int command, void *message) {
	#ifdef DEBUG
	__asm("mov r0, %[cmd];"
	"mov r1, %[msg];"
	"bkpt #0xAB"
		:
		: [cmd] "r" (command), [msg] "r" (message)
		: "r0", "r1", "memory");
	#endif
}

Jak widzisz funkcję tę opakowałem w #ifdefa. Dzięki temu mogę szybko wyłączyć semihosting jeśli nie chcę go użyć. Do tego dochodzi jeszcze cała otoczka w postaci funkcji printujących.

#define INT_DIGITS 19 /* enough for 64 bit integer */

char *itoa(int i)
{
	static char buf[INT_DIGITS + 2];
	char *p = buf + INT_DIGITS + 1; /* '\0' */
	if (i >= 0) {
		do
		{
			*--p = '0' + (i % 10);
			i /= 10;
		} while (i != 0);
		return p;
	}
	else { /* i < 0 */
		do 
		{
			*--p = '0' - (i % 10);
			i /= 10;
		} while (i != 0);
		*--p = '-';
	}
	return p;
}

void prints( const char* str )
{

	uint32_t len = strlen(str);
	uint32_t i = 0;

	for( i = 0; i < len; i+=4)
	{

		uint32_t buflen = 4;
		if( i+4 >= len ) buflen = len-i;
		size_t m[] = { 2/*stderr*/, (size_t)str, buflen/sizeof(char) };
		send_command(0x05/* some interrupt ID */, m);
		str+=4;
	}
}

void printc( char c )
{
	char buf[2];

	buf[0] = c;
	buf[1] = 0;

	prints(buf);
}

void printi( int32_t i )
{
	prints( itoa(i) );
}

char *convert(unsigned int num, int base)
{
static char Representation[]= "0123456789ABCDEF";
	static char buffer[50];
	char *ptr;

	ptr = &buffer[49];
	*ptr = '\0';

	do
	{
		*--ptr = Representation[num%base];
		num /= base;
	}while(num != 0);

	return(ptr);
}

void print(char* format, ...)
{

	char *traverse;
	unsigned int i;
	char *s;
	double d;

	va_list arg;

	va_start(arg, format);

	/// Pack buf per 4 characters (last is always NULL)
	char buf[5];
	memset(buf, 0, 5);
	int t = 0;

	for(traverse = format; *traverse != '\0'; traverse++)
	{
		if (*traverse != '%')
		{
			if( t == 5 )
			{
				prints(buf);
				memset(buf, 0, 5);
				t = 0;
			}

			buf[t%5] = *traverse;
			t++;
			continue;
		}

		prints(buf);
		memset(buf, 0, 5);
		t = 0;

		traverse++;

		/// Module 2: Fetching and executing arguments
		switch(*traverse)
		{
			/// Fetch char argument
			case 'c' : i = va_arg(arg,int);
				printc(i);
				break;

			/// Fetch Decimal/Integer argument
			case 'd' : i = va_arg(arg,int);
				if(i<0)
				{
					i = -i;
					printc('-');
				}
				prints(convert(i,10));
				break;

			/// Fetch floating point argument
			case 'f' : d = va_arg(arg, double);
				if(d<0)
				{
					d = -d;
					prints("-");
				}
				i = (unsigned int)d;
				prints(convert(i, 10));
				printc('.');

				d = d - i;
				if (d*10.0 < 1.0)
					printc('0');
				if (d*100.0 < 1.0)
					printc('0');

				/// Round to 3 decimal places
				prints(convert((int)round(d*1000.0), 10));
				break;

			/// Fetch Octal representation
			case 'o': i = va_arg(arg,unsigned int);
				prints(convert(i,8));
				break;

			/// Fetch string
			case 's': s = va_arg(arg,char *);
				prints(s);
				break;

			/// Fetch Hexadecimal representation
			case 'x': i = va_arg(arg,unsigned int);
				prints(convert(i,16));
				break;
		}
	}

	prints(buf);
	memset(buf, 0, 5);

	va_end(arg);
}

[/cc]

Gdy mamy już gotowy kod, trzeba skonfigurować SW4STM32, aby odbierał on komunikaty w gdb. Otwórz konfiguracje profili debugowania.

W zakładce Startup używanego przez Ciebie profilu dodaj

monitor arm semihosting enable

w polu Run Commands.

Pamiętaj o tym, aby komentować kod do semihostingu jeśli go nie używasz. Możesz to wykonać np. za pomocą prostego define’a jak jak wspominałem wyżej.

Po wykonaniu tych kroków możemy printować w kodzie np. testowo w głównej pętli programu między wysyłaniek komunikatu przez UART, a cyklicznym mignięciem diody:

while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
  uint8_t message[] = "UART over ST-Link \n\r";
  HAL_UART_Transmit(&huart2, message, sizeof(message)-1, 100);

  print("Semihosting test \n");
  HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
  HAL_Delay(500);
}

/* USER CODE END 3 */

 

Uruchomienie programu w trybie debugowania generuje cykliczne wypisywania na konsole gdb testowego komunikatu.

Przyznaj, że jest to świetne. Nie potrzebujesz wolnego UARTa do prostego debugowania – wystarczy ST-Link, którego masz zawsze!

Jeśli podobał Ci się ten wpis polub lub udostępnij go dalej. Będę bardzo wdzięczny za każdą aktywność.

Jak zwykle kod źródłowy użyty we wpisie znajdziesz na GitHubie: link

Jeśli zauważyłeś jakiś błąd, nie zgadzasz się z czymś lub po prostu uważasz, że chciałbyś podystkutować w tym temacie, napisz komentarz. Pamiętaj, że dyskusja ma być kulturalna i zgodna z zasadami języka polskiego.

5/5 - (7 votes)

Podobne artykuły

.

8 komentarzy

Tomasz · 06/11/2020 o 22:36

Witaj, czy pin “SWO” jest potrzebny przy debugowaniu? Czy można go bezwzględnie pominąć?

    Mateusz Salamon · 07/11/2020 o 10:01

    Można pominąć.

Dariusz · 01/03/2020 o 14:43

“…Aby użyć UART na zewnątrz bez wyłamywania ST-Linka należy wylutować zworki SB13 i SB14 znajdującej się na spodzie PCB…”, albo ustawić wejścia na PA2,PA3

    Mateusz Salamon · 10/03/2020 o 09:45

    Ale PA2, PA3 na mikrokontrolerze na Nucleo, tak? Można w sumie i tak, nie pomyślałem o tym, ale musisz na szybko robić i wrzucać projekt na Nucleowy MCU 😀 Ma to plus, że jest łatwo odwracalne, a lutowanie faktycznie może być mocno angażujące.

SaS · 03/12/2019 o 13:40

Z ST-Link V2-1 9ale -12 więc chińskie klony odpadają) można bezpłatnie, legalnie, zrobić J-LINK EDU! Jak? Tak: https://mikrokontroler.pl/2016/05/06/stlinkreflash-interfejs-j-link-w-zestawach-stm32-nucleo-i-discovery/
Zalety? 8192 pułapki anie zależnie od rdzenia 3 do 9. Co istotne, proces jest odwracalny! Jak J-LINK się nie podoba można zrobić go ponownie ST-Linkiem.

    Mateusz Salamon · 08/12/2019 o 15:06

    Słyszałem kiedyś o tym. Chętnie wypróbuję i opiszę na blogu. Dzięki!

      SaS · 12/12/2019 o 20:10

      Wypróbowałem, działa. Różnica w stosunku do ST-Link duża, głównie w sensie liczby półłapek.

Dodaj komentarz

Avatar placeholder

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *