Otóż wpadłem na taki pomysł, żeby stworzyć rozbudowany system diagnostyki maszyny pracującej pod kontrolą LinuxCNC. Brzmi to trochę skomplikowanie, ale jest dość proste, chodzi o zgłoszenie alarmu na przykład kiedy napięcie jakiegoś zasilacza nie będzie się mieścić w założonych granicach (równie dobrze można mierzyć inne wielkości, ale w elektronice i tak konwertuje się je do napięcia).
Wybór padł na STM32, bo płytki z tymi procesorami można kupić za kilka-kilkadziesiąt złotych, a potrafią mieć nawet po kilkadziesiąt pinów wejścia/wyjścia. Z zasady STM32 mają wbudowane przetworniki analogowo-cyfrowe, więc nie potrzeba żadnych skomplikowanych układów dodatkowych, wystarczy układ odpowiednio zaprogramować.
To tyle tytułem wstępu.
Ponieważ STM32 dość długo nie używałem, to pewne rzeczy wyleciały mi z głowy, więc postanowiłem znaleźć w necie gotowe przykłady na (jakby nie było) jedną z podstawowych użyteczności współczesnych mikrokontrolerów.
Znalazłem mnóstwo takich jak poniższy, których nie próbujcie zrozumieć, bo i tak nie działają, kompilator pluje błędami bo mu czegoś brakuje itd. Przewińcie tylko i zwróćcie uwagę na długość i stopień skomplikowania kodu:
Kod: Zaznacz cały
// some sytm32duino specific code not important for understanding
// --------------------------------------------------------------------------------------
#ifndef ADC_CLOCK_DIV
#ifdef ADC_CLOCK_SYNC_PCLK_DIV4
#define ADC_CLOCK_DIV ADC_CLOCK_SYNC_PCLK_DIV4
#elif ADC_CLOCK_SYNC_PCLK_DIV2
#define ADC_CLOCK_DIV ADC_CLOCK_SYNC_PCLK_DIV2
#elif defined(ADC_CLOCK_ASYNC_DIV1)
#define ADC_CLOCK_DIV ADC_CLOCK_ASYNC_DIV1
#endif
#endif /* !ADC_CLOCK_DIV */
#ifndef ADC_SAMPLINGTIME
#if defined(ADC_SAMPLETIME_8CYCLES_5)
#define ADC_SAMPLINGTIME ADC_SAMPLETIME_8CYCLES_5;
#elif defined(ADC_SAMPLETIME_12CYCLES_5)
#define ADC_SAMPLINGTIME ADC_SAMPLETIME_12CYCLES_5;
#elif defined(ADC_SAMPLETIME_13CYCLES_5)
#define ADC_SAMPLINGTIME ADC_SAMPLETIME_13CYCLES_5;
#elif defined(ADC_SAMPLETIME_15CYCLES)
#define ADC_SAMPLINGTIME ADC_SAMPLETIME_15CYCLES;
#elif defined(ADC_SAMPLETIME_16CYCLES)
#define ADC_SAMPLINGTIME ADC_SAMPLETIME_16CYCLES;
#elif defined(ADC_SAMPLETIME_19CYCLES_5)
#define ADC_SAMPLINGTIME ADC_SAMPLETIME_19CYCLES_5;
#endif
#endif /* !ADC_SAMPLINGTIME */
// --------------------------------------------------------------------------------------
ADC_HandleTypeDef hadc;
void setup()
{
pinMode(13,OUTPUT);
pinMode(A0,INPUT);
pinMode(A1,INPUT);
pinMode(A2,INPUT);
Serial.begin(115200);
delay(2000);
MX_ADC_Init();
}
int value[3]={0};
void loop()
{
digitalWrite(13, HIGH);
//adc_read(&hadc, value, 3);
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 1); // polling timeout 1ms - cannot go lower
value[0] = HAL_ADC_GetValue(&hadc);
HAL_ADC_PollForConversion(&hadc, 1);
value[1] = HAL_ADC_GetValue(&hadc);
HAL_ADC_PollForConversion(&hadc, 1);
value[2] = HAL_ADC_GetValue(&hadc);
digitalWrite(13, LOW);
Serial.print(value[0]);
Serial.print("\t");
Serial.print(value[1]);
Serial.print("\t");
Serial.println(value[2]);
}
void MX_ADC_Init()
{
ADC_ChannelConfTypeDef sConfig;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_DIV;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ENABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.NbrOfConversion = 3;
hadc.Init.DiscontinuousConvMode = ENABLE;
hadc.Init.NbrOfDiscConversion = 3;
// this is the important bit
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DMAContinuousRequests = DISABLE;
HAL_ADC_Init(&hadc);
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLINGTIME;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = 3;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
/* Configure ADC GPIO pin */
pinmap_pinout(analogInputToPinName(A0), PinMap_ADC);
pinmap_pinout(analogInputToPinName(A1), PinMap_ADC);
pinmap_pinout(analogInputToPinName(A2), PinMap_ADC);
}
Nie tęsknię za tamtymi czasami, bo były wyjątkowo paskudne, ale wyniosłem z nich kilka umiejętności, których próżno szukać u współczesnych programistów, którzy wychodzą z założenia, że pamięć jest tania, więc jak program ma megabajt, to jest krótki..
Otóż wklejony powyżej kod to HAL, czyli opis oderwany od sprzętu. Ma on tylko jedną zaletę - łatwo można go przenieść na inny sprzęt. Po prostu nie odwołuje się bezpośrednio do rejestrów procesora, tylko do plików konfiguracyjnych, które się odwołują do innych plików konfiguracyjnych, żeby na końcu było wiadomo co gdzie wpisać.
Ja nie mam nic przeciwko HAL, kiedy ktoś pisze długie i skomplikowane programy, których przepisanie na nowy model procesora byłoby niesamowitą męczarnią.
Ale żeby używać HAL do tak prostych rzeczy jak włączenie DAC?
Jako programista bardzo starej daty wiem, że to raptem wpisanie kilku bitów do kilku rejestrów, nie raz to robiłem, tylko pamięć już nie ta...
Naprawdę, znalezienie kodu który mnie interesował było szukaniem igły w stogu siana, wszędzie HAL,HAL i HAL...
W końcu coś znalazłem, trochę przerobiłem i zmierzyłem napięcie na podłączonym potencjometrze:
Kod: Zaznacz cały
//ADC registers chapter 11.12 on page 237 of
//rm0008-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
void setup() {
Serial.begin(115200);
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // make sure the ADC is clocked
ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CONT; // Turn on ADC, enable continuos mode
ADC1->CR2 |= ADC_CR2_ADON; // Turn on conversion
}
void loop() {
Serial.println(ADC1->DR);
delay(200);
}
Natomiast nie trzeba być programistą żeby ocenić który kod jest krótszy, prostszy, a więc i łatwiejszy do zrozumienia...
Powyższe to tylko przykład ogólnej tendencji do komplikowania wszystkiego co się tylko da...
Wszystko trzeba tak zagmatwać, żeby nikt niczego nie rozumiał i do wszystkiego trzeba używać narzędzi tak skomplikowanych żeby nikt nie potrafił z nich skorzystać...
Moim zdaniem, taki postęp nas zabije.