[Mój sposób na...] Komunikacja z kodowaniem Manchester

Tu możesz pisać o swoich problemach z pisaniem programów w języku C/C++ dla STM.
Awatar użytkownika
ZbeeGin
User
User
Posty: 492
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: Śląsko-Zagłębiowska Metropolia
Kontaktowanie:

[Mój sposób na...] Komunikacja z kodowaniem Manchester

Postautor: ZbeeGin » wtorek 30 lip 2019, 23:14

Trochę teorii.

Manchester, a w zasadzie kodowanie Manchester to sposób na przekazanie szeregowo jakiegoś cyfrowego sygnału, tak by można w odbiorniku odzwierciedlić dokładnie to samo co wyszło z nadajnika. Możemy po prostu wysyłać dane w regularnych odstępach czasu co powinno nam zapewnić bezbłędną transmisję. No... Nie do końca. Udałoby się to, gdyby oba urządzenia były ze sobą precyzyjnie zsynchronizowane, ale nawet stosując najdokładniejsze rezonatory kwarcowe nie jesteśmy w stanie tego warunku spełnić, zwłaszcza na dłuższą metę.

Można synchronizować oba urządzenia przesyłając po osobnym przewodzie sygnał zegarowy. Wystarczy by ten sygnał nadawała zawsze strona nadawcza w danej chwili pełniąca rolę układu nadrzędnego. Jeśli oba przewody: danych i zegarowy będą tej samej długości to nie wprowadzi to żadnych opóźnień czasowych między nimi. Tak. Tylko nie zawsze mamy możliwość wyprowadzenia tego dodatkowego przewodu, lub miejsca gdzie go podłączyć...

A jakby tak w jakiś magiczny sposób połączyć oba sygnały w jeden? I tu dochodzimy do sedna systemu kodowania Manchester. On właśnie daje tą możliwość by jednym przewodem przesyłać dane i sygnał zegarowy jednocześnie. Wystarczy jedno proste założenie. Sygnał zegarowy musi być dwa razy szybszy niż prędkość transmisji bitów. Wtedy prostą operacją logiczną możemy oba sygnały połączyć w jeden i co najważniejsze, po stronie odbiorczej znów jeśli trzeba je rozdzielić! Przeanalizujmy obrazek:

Obrazek (Źródło. Wikipedia)

Jak widać mamy sygnał zegarowy i sygnał danych. Nałożyliśmy je na siebie stosując funkcję logiczną XOR (suma wyłączna) otrzymując sygnał wyjściowy, charakteryzujący się pewnymi właściwościami:
1. Poszczególne bity danych będą reprezentowane nie przez stałe stany logiczne, ale przez kierunek przejścia z jednego stanu w drugi. Dla "1" logicznej otrzymamy przejście 1→0, a dla "0" logicznego przejście 0→1. Dzięki temu z nich możemy wyłuskać sygnał zegarowy, lub odpowiednio przygotowując protokół doprowadzić do tego by prędkość danych dała się automatycznie określić.
2. Nawet jeśli w ciągu danych pojawią się ciągi takich samych bitów, nic to nie zmienia. Dalej nasz sygnał zegarowy daje się z takiego ciągu danych wyłuskać bo przejścia będą dalej nadawane. Jest to tak zwany sygnał NRZ (Non Return to Zero).

To tyle teorii, czas na małą implementację.

W jednym z urządzeń zaszła potrzeba by mogło się ono dwukierunkowo porozumieć ze sterownikiem, który rozumie właśnie kodowanie Manchester. Może nadawać i odbierać z "oszałamiającą" prędkością 16 bitów na sekundę. Ramka danych składa się z 5 bajtów: preambuły - zawsze 0xFF, 3 bajtów danych i bajtu sumy kontrolnej.
Jak można łatwo policzyć to jeden bit danych zajmuje 60ms, a sygnał zegarowy jaki jest potrzebny to 30ms. Transmisja jednej ramki danych trwa ok. 2,5 sekundy. Jest to wartość stała, więc nie musimy dokonywać analizy sygnału by za każdym razem określać jaka jest prędkość. Choć można to zrobić wykorzystując... preambułę, która przez czas potrzeby na nadanie 8 bitów dokładnie odzwierciedla nam sygnał zegarowy nadajnika.

Już wiemy, że potrzebny nam będzie licznik, a w zasadzie dwa, który wygeneruje nam odcinki czasu 30ms. Jeden z nich - TIM6 - będzie taktował sygnał nadawany z urządzenia do sterownika linią TX, a drugi - TIM3 - będzie służył do skanowania stanu linii odbiorczej RX. Konfiguracja będzie dość prosta:

Code: Select all

TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim6;

/* TIM3 init function */
void MX_TIM3_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};

__HAL_RCC_TIM3_CLK_ENABLE();

htim3.Instance = TIM3;
htim3.Init.Prescaler = 31999;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 31;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&htim3);

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig);
HAL_TIM_OC_Init(&htim3);

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);

sConfigOC.OCMode = TIM_OCMODE_TIMING;
sConfigOC.Pulse = 15;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_OC_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
}

/* TIM6 init function */
void MX_TIM6_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};

__HAL_RCC_TIM6_CLK_ENABLE();

htim6.Instance = TIM6;
htim6.Init.Prescaler = 31999;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 29;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&htim6);

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig)
}


Ponieważ zegar taktujący procesora jak i peryferii wynosi 32MHz to prescaler został ustawiony właśnie na 32000. Dziwić Was może to, że jeden z liczników liczy dokładnie 30ms, a drugi 32ms. Tak. Jest to zamierzone działanie, które wyjaśnię później.
Dodatkowo w liczniku "odbiorczym" zostało włączone generowanie przerwań z porównania. Ono posłuży nam jako element wyzwalający fragment kodu, który będzie skanował aktualny stan linii RX. I będzie to robił mniej więcej na w środku ustalonego stanu tej linii.
Pozostaje nam tylko wystartować liczniki i ich przerwania.

Code: Select all


HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_OC_Start_IT (&htim3, TIM_CHANNEL_1);

HAL_TIM_Base_Start_IT(&htim6);


Nadawanie będzie bardzo proste w realizacji. Weźmiemy kolejne bajty, zakodujemy je do postaci NRZ i w równych odcinkach czasu będziemy wysłać kolejne stany na linię TX. No, tak. "Przekodujemy", ale jak?

Code: Select all

/**
* @brief Zmiana jednego bajtu w słowo NRZ
* @param bValue : bajt do zmiany
* @retval uint16 : zakodowany bajt w słowie
*/
static uint16_t encode_nrz(uint8_t bValue) {

uint16_t ret_val = 0;
uint8_t bit_count = 8;

while(bit_count--) {
/* Robimy miejsce na następną parę bitów */
ret_val <<= 2;
/* Jeśłi bit == 1 wprowadzamy 1->0,
* inaczej wprowadzamy 0->1
* */
ret_val |= (bValue & 0x80) ? 0x0002 : 0x0001;
bValue <<= 1;
}
return(ret_val);
}

/**
* @brief Zamiana słowa NRZ w bajt
* @param wValue : słowo NRZ
* @retval uint8 : odkodowany bajt
*/
static uint8_t decode_nrz(uint16_t wValue) {

uint8_t ret_val = 0;
uint8_t bit_count = 8;

while(bit_count--) {
/* Robimy miejsce na następny bit */
ret_val <<= 1;
/* Jak wykryjemy zmianę 1->0 wstawiamy 1,
* inaczej wstawiamy 0
* */
ret_val |= ((wValue & 0xC000) == 0x8000) ? 0x01 : 0x00;
wValue <<= 2;
}
return(ret_val);
}


Właśnie tak. Za pomocą tych dwóch funkcji możemy przetwarzać dane do nadania jak i dane odebrane.

Teraz właściwe nadawanie. Przygotujemy sobie pewne zmienne nadajnika tworzące strukturę oraz bufor gdzie wpisujemy dane do nadania. Samym zaś nadawaniem zajmie się licznik poprzez jego przerwanie z przepełnienia.

Code: Select all

/**
* @brief Długość ramki
*/
#define FRAME_LEN (5)

/**
* @brief Stan nadajnika
*/
typedef enum {
MTX_IDLE, /* Nieaktywny */
MTX_TRANSMIT /* Transmisja w trakcie */
} MTXStateTypeDef;


/**
* @brief Struktura przechowująca istotne dane nadajnika
*/
typedef struct {
uint32_t index; /* Aktualne słowo */
uint32_t bit; /* Aktualny bit w słowie */
MTXStateTypeDef state; /* Stan nadajnika */
uint16_t nrzframe[FRAME_LEN]; /* Dane surowe do nadania */
} TransmitDataTypeDef;

volatile TransmitDataTypeDef tx_struct = {0};

/**
* @brief Basic frame (template) buffers
*/
static uint8_t tx_man_frame[FRAME_LEN] = {
0xff, /* preambuła, zawsze 0xff */
0x00, /* data byte 1 */
0x00, /* data byte 2 */
0x00, /* data byte 3 */
0x00 /* suma kontrolna */
};

/**
* @brief Wspólny callback przepełnienia liczników
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

/* TIM6 jest używany jako nadajnik, więc najpierw sprawdzamy
* czy nie leci aktualnie transmisja, by ją kontynuować
*/
if(htim->Instance == TIM6) {

if(tx_struct.state == MTX_TRANSMIT) {
/* Sprawdzenie bitów by móc przejść na następne słowo */
if(tx_struct.bit > 15) {
tx_struct.bit = 0;
tx_struct.index++;
}

/* Czy wysłaliśmy już wszystko */
if(tx_struct.index == FRAME_LEN) {
/* Zmiana aktywności */
tx_struct.state = MTX_IDLE;

/* Zachowaj stan niski po transmisji */
HAL_GPIO_WritePin(TX_Port, TX_Pin, GPIO_PIN_RESET);
}
else {
/* Wypuść kolejny bit na linię */
if(tx_struct.nrzframe[tx_struct.index] & 0x0001) {
HAL_GPIO_WritePin(TX_Port, TX_Pin, GPIO_PIN_RESET);
}
else {
HAL_GPIO_WritePin(TX_Port, TX_Pin, GPIO_PIN_SET);
}
/* Przesuwamy się na kolejny bit */
tx_struct.nrzframe[tx_struct.index] >>= 1;
tx_struct.bit++;
/* Aktualny bit skończony - Wychodzimy */
}
}
}

(...)

}


Nadawanie będzie się odbywać w tle z automatu. Jedyne co musimy zrobić to zapisać dane do bufora i "zakodować" w NRZ i poustawiać flagi początkowe. Przykładowo

Code: Select all

static void encode_frame(void) {

/* Zamiana na NRZ */
for(uint32_t idx = 0; idx < FRAME_LEN; idx++) {
tx_struct.nrzframe[idx] = encode_nrz(tx_man_frame[idx]);
}

if(tx_struct.state == MTX_IDLE) {
/* Reset indeksów i jazda */
tx_struct.index = 0;
tx_struct.bit = 0;
tx_struct.state = MTX_TRANSMIT;
}
};



Odbiór już będzie bardziej skomplikowany. Będziemy musieli wykryć trzy zdarzenia:

1. Gdy coś pojawi się na linii RX musimy zsynchronizować licznik z przebiegiem nadchodzącym. Będziemy go resynchronizować za każdą wykrytą zmianą stanu linii RX, a gdyby tej zmiany nie było licznik musi być w stanie sam się napędzić, przynajmniej na czas dwóch okresów. Właśnie dlatego ustaliłem nieco dłuższy czas w konfiguracji licznika TIM3.
2. Przepełnienie licznika, które posłuży nam nie tylko jako źródło taktowania, ale również pozwoli nam wykryć ciszę na linii dłuższą niż 3 okresy licznika, która w trakcie transmisji nie powinna wystąpić - patrz przebieg sygnału Manchester.
3. Porównanie z licznika, które w miarę precyzyjnie określi nam czas, gdzie stan linii powinien być już stabilny, by móc go odczytać.

Code: Select all

typedef enum {
MRX_IDLE, /* Cisza na linii */
MRX_SCAN, /* Rozpoczęto skanowanie stanów */
MRX_COMPLETE, /* Ramka skompletowana */
MRX_FRAME_ERROR = 128 /* Wykryto błąd - ramka niekompletna */
} MRXStateTypeDef;

/**
* @brief Struktura przechowująca dane odbiornika
*/
typedef struct {
uint32_t index; /* Aktualne słowo */
uint32_t bit; /* Aktualny bit */
MRXStateTypeDef state; /* Stan transmisji */
uint16_t nrzframe[FRAME_LEN]; /* Surowe dane przychodzące */
uint8_t rampcount; /* Licznik przepełnień */
uint16_t linenow; /* Aktualny stan linii RX */
} ReciveDataTypeDef;

volatile ReciveDataTypeDef rx_struct = {0};

static uint8_t rx_man_frame[FRAME_LEN] = {0};
/* Dane takie same jak nadawczy bufor */


/**
* @brief Wspólny callback EXTI
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
/* Reaguj na zmiany na linii RX - oba zbocza */
if(GPIO_Pin == RX_Pin) {
/* Resynchronizuj licznik */
HAL_TIM_GenerateEvent(&htim3, TIM_EVENTSOURCE_UPDATE);
/* Skasuj licznik przepełnień */
rx_struct.rampcount = 0;

/* Jeśli jest to pierwsza zmiana po okresie ciszy zaznacz, że przychodzi transmisja */
if(rx_struct.state == MRX_IDLE) {
rx_struct.state = MRX_SCAN;
rx_struct.bit = 0;
rx_struct.index = 0;
}
}
}


/**
* @brief Wspólny callback przepełnienia liczników
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

(...)

/* TIM3 używamy jako taktowanie odbiornika */
if(htim->Instance == TIM3) {
/* Jak stan linii się nie zmieniał przez co najmniej 3 okresy i trwa odbiór, zgłoś błąd */
if((++rx_struct.rampcount > 3) && (rx_struct.state == MRX_SCAN)) {
rx_struct.state = MRX_FRAME_ERROR;
}
}

}

/**
* @brief Wspólny callback porównania liczników
*/
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim) {

/* Porównanie z TIM3 używane jest do skanowania stanu linii w środku póbitu */
if((htim->Instance == TIM3) && (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)) {
rx_struct.linenow = (HAL_GPIO_ReadPin(RX_Port, RX_Pin) == GPIO_PIN_SET) ? 0x0000 : 0x8000;

if(rx_struct.state == MRX_SCAN) {
/* Sprawdź bity czy nie przeskoczyć na kolejny bajt */
if(rx_struct.bit > 15) {
rx_struct.bit = 0;
rx_struct.index++;
}

/* Czy odebraliśmy wystarczającą ilość danych */
if(rx_struct.index == FRAME_LEN) {
/* Ustaw, że dane kompletne */
rx_struct.state = MRX_COMPLETE;
}
else {
/* Zrób miejsce na kolejny bit */
rx_struct.nrzframe[rx_struct.index] >>= 1;
rx_struct.bit++;
/* Wprowadź jego stan do danych surowych */
rx_struct.nrzframe[rx_struct.index] |= rx_struct.linenow;
/* Bit gotowy - Wychodzimy */
}
}
}
}


I tu podobnie jak wykryjemy, że dane są kompletne to rozkodujemy ciąg NRZ na dane właściwe. Oczywiście powinniśmy je jakoś przeanalizować - przede wszystkim sprawdzając sumę kontrolną.

Code: Select all

static uint8_t decode_frame(void)
{
uint32_t err_code = 0;

for(uint32_t idx = 0; idx < FRAME_LEN; idx++) {
rx_man_frame[idx] = decode_nrz(rx_struct.nrzframe[idx]);
err_code = check_sum(rx_man_frame);

return err_code;
}



Na zakończenie.

Oczywiście nie jest to kompletny kod, a tylko informacje jak wykorzystać sprzęt by transmisja "sama się napędzała". Jeśli macie jakieś inne pomysły czy może lepszy sposób to dyskusję uważam za otwartą.
Ostatnio zmieniony niedziela 04 sie 2019, 07:11 przez ZbeeGin, łącznie zmieniany 1 raz.

Marhef
Posty: 24
Rejestracja: czwartek 30 maja 2019, 11:43

Re: Manchester - Jak z nim wygrać (Nie na boisku)?

Postautor: Marhef » środa 31 lip 2019, 10:58

A nie łatwiej reagować na zbocza? W przerwaniu? I mierzyć czas pomiędzy nimi?

Awatar użytkownika
ZbeeGin
User
User
Posty: 492
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: Śląsko-Zagłębiowska Metropolia
Kontaktowanie:

Re: Manchester - Jak z nim wygrać (Nie na boisku)?

Postautor: ZbeeGin » środa 31 lip 2019, 11:12

Mamy tu reakcję na zbocza przecież i aż 3 współpracujące ze sobą przerwania...

Marhef
Posty: 24
Rejestracja: czwartek 30 maja 2019, 11:43

Re: Manchester - Jak z nim wygrać (Nie na boisku)?

Postautor: Marhef » czwartek 01 sie 2019, 13:20

Nie do końca o to mi chodziło. Nie łatwiej mierzyć czas pomiędzy zboczami?
Najpierw "łapiesz" preambułę, czyli 16 jedynek.
I teraz tak, po 16 jedynce mierzysz czas pomiędzy zboczem opadającym i narastającym. Jeżeli jest 30 ms, to kolejny bit to 1, jeżeli 60 ms, to kolejny bit to 0. I mierząc czas pomiędzy kolejnymi zboczami wiesz, jakie masz wartości.
Bo z tego, co zrozumiałem, sprawdzasz stan linii co określony odstęp czasu. "Po mojemu" jest odwrotnie. Wyłapujesz zbocza mierzysz czas pomiędzy nimi.

Myślałeś o tym, żeby wykrywać częstotliwość sygnału i dopasować do niej pomiar?


I wiem, zacząłem od czepiania się, za co przepraszam. Dobrze, że są jeszcze ludzie, którym chce się przekazać swoją wiedzę.

Awatar użytkownika
ZbeeGin
User
User
Posty: 492
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: Śląsko-Zagłębiowska Metropolia
Kontaktowanie:

Re: Manchester - Jak z nim wygrać (Nie na boisku)?

Postautor: ZbeeGin » piątek 02 sie 2019, 08:29

Marhef pisze:Nie do końca o to mi chodziło. Nie łatwiej mierzyć czas pomiędzy zboczami?
Najpierw "łapiesz" preambułę, czyli 16 jedynek.

Niestety będzie tylko 8 jedynek ;) Ale nic to nie zmienia, bo w zasadzie już po trzech półbitach preambuły można by w miarę dokładnie określić jaki jest maksymalny czas tego półbitu.

Marhef pisze:I teraz tak, po 16 jedynce mierzysz czas pomiędzy zboczem opadającym i narastającym. Jeżeli jest 30 ms, to kolejny bit to 1, jeżeli 60 ms, to kolejny bit to 0. I mierząc czas pomiędzy kolejnymi zboczami wiesz, jakie masz wartości.

Hmmm... Nie tak prędko. Spójrz jak wyglądają fragmenty przebiegu wyjściowego przy niekorzystnym dla Twojego algorytmu układzie bitów, np. '10101'. Analiza czasowa w takiej sytuacji dałaby ciąg '10000', bo pierwszą jedynkę byś wykrył - czas 30ms, potem byś wykrył prawidłowo pierwsze zero z uwagi na czas następnego zbocza 60ms, ale znów miałbyś 60ms pomiędzy następnymi zboczami, co algorytm uznałby za stan '0'.

Marhef pisze:Myślałeś o tym, żeby wykrywać częstotliwość sygnału i dopasować do niej pomiar?

Tak. Nawet w programie - ale nie tu, tylko w moim pełnym - dalej mam jeszcze jeden stan MRX_AUTOBAUD - który by wskazywał, że akurat program analizuje preambułę by wykryć jak ustawić wartość MAX licznika i MAX / 2 dla rejestru porównania. Ale skoro jest niemalże pewne, że sterownik będzie się trzymał pewnej prędkości nadawania to nie implementowałem tego.
Co więcej, w sterowniku z którym to się komunikuje też nie mają tego mechanizmu, bo jak zmieniłem czasy taktowania nadawania o 20% to już nie odbierał danych.

Dlatego trzymać się będę zdania, że skoro dostarczyli nam taki sterownik jak teraz i do niego dostosowaliśmy nasze urządzenie to nie będę niepotrzebnie komplikować kodu. Chcecie coś zmienić to... szlaban na to. Trzymamy się założeń.

Marhef
Posty: 24
Rejestracja: czwartek 30 maja 2019, 11:43

Re: Manchester - Jak z nim wygrać (Nie na boisku)?

Postautor: Marhef » piątek 02 sie 2019, 09:59

ZbeeGin pisze:
Marhef pisze:Nie do końca o to mi chodziło. Nie łatwiej mierzyć czas pomiędzy zboczami?
Najpierw "łapiesz" preambułę, czyli 16 jedynek.

Niestety będzie tylko 8 jedynek ;)
Jak piszesz, nawet 8 jest za dużo ;) nie wiem, gdzie znalazłem drugie 8...
ZbeeGin pisze:
Marhef pisze:I teraz tak, po 16 jedynce mierzysz czas pomiędzy zboczem opadającym i narastającym. Jeżeli jest 30 ms, to kolejny bit to 1, jeżeli 60 ms, to kolejny bit to 0. I mierząc czas pomiędzy kolejnymi zboczami wiesz, jakie masz wartości.

Hmmm... Nie tak prędko. Spójrz jak wyglądają fragmenty przebiegu wyjściowego przy niekorzystnym dla Twojego algorytmu układzie bitów, np. '10101'. Analiza czasowa w takiej sytuacji dałaby ciąg '10000', bo pierwszą jedynkę byś wykrył - czas 30ms, potem byś wykrył prawidłowo pierwsze zero z uwagi na czas następnego zbocza 60ms, ale znów miałbyś 60ms pomiędzy następnymi zboczami, co algorytm uznałby za stan '0'.
Pisałem na szybko i nie uwzględniłem wszystkiego. Podstawą do takiego algorytmu jest przełączanie się pomiędzy wykrywaniem zbocza narastającego i opadającego. Jeżeli jest 60 ms pomiędzy zboczem narastającym i opadającym, to masz '01', jeżeli pomiędzy zboczem opadającym i narastającym, to masz '10'. Teraz nie mam możliwości, żeby to jakoś dokładniej rozpisać, ale mam nadzieję, że jest to w miarę jasno napisane.
ZbeeGin pisze:Dlatego trzymać się będę zdania, że skoro dostarczyli nam taki sterownik jak teraz i do niego dostosowaliśmy nasze urządzenie to nie będę niepotrzebnie komplikować kodu. Chcecie coś zmienić to... szlaban na to. Trzymamy się założeń.
Masz rację. Moje pytania dotyczyły bardziej sytuacji, kiedy ktoś chce napisać uniwersalną obsługę. Ale jeśli ma to być do konkretnego rozwiązania, i działa(!), to po co sobie komplikować życie?

Rozumiem, że na kod nie ma praw autorskich i można go wykorzystać w swoich projektach?

Awatar użytkownika
ZbeeGin
User
User
Posty: 492
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: Śląsko-Zagłębiowska Metropolia
Kontaktowanie:

Re: Manchester - Jak z nim wygrać (Nie na boisku)?

Postautor: ZbeeGin » piątek 02 sie 2019, 13:18

To jest tylko pokazana konkretna idea - nieco uproszczona niż to co mam w urządzeniu więc nie widzę konfliktu interesów.

Pod względem stricte praw to nie ma czegoś takiego, że "utwór" nie posiada praw autorskich. One są, powstały w chwili utrwalenia, i są niezbywalne. Ale to już temat na odrębną dyskusję...


Wróć do „Programowanie STM w C/C++”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 8 gości