Aktiivinen analogielektroniikka 2 - keskiviikkoisin 18:00 syksy 2016


#1

Parilla ensimmäisellä kerralla jatkettiin keväällä kyhätyn moottorinohjauspiirin tutkimista. Samalla muiteltiin mieleen Bode-kuvaajan olemusta. Niistä ohessa kurssimateriaalia.

Desibeli
Analoginen moottorisäätäjä

Aloiteltiin yhteisprojektia joss tarkoituksena tuottaa pieni protolauta sulautettua prosessoria varten. Laudalle toteutettaisi mahdollisimman yleiskäyttöinen jännitteensyöttö ja tarvittavaa liitäntäelektronikkaa jotta saadaan helposti aikaan monipuolisia kokeilukytkentöjä. Tämä tehtävä aloitettiin käymällä läpi piirilevyn valmistuksessa vastaan tulevia juttuja, mistä käytiin läpi tähän linkattu aiempi esitys.

PCB työnkulku


#2

Valittiin protolaudalle prosessoriksi STM32F303 Cortex-M4F.
LInkissä opas kehitysympäristön pystyttämiseen ja liikkeelle lähtöön ST:n prosessoreilla.


#3

Huomenna 21.9. ei pidetä Analogielektroniikan kurssia, sillä Kremmen on tutustumassa ehkä läbille hankittavaan metallisorviin.


#4

…joka sitten päätyi hankittavaksi. Että ei ollut hukkareissu :slight_smile:


#5

Kurssilaisille tiedoksi: NucleoF303-laudat tulivat. Varakiteen kera kustannus nuppia kohti on 13,50€ eli ei paha.

Kurssin oppikirjan mukainen toolchain toimii Windowsissa virheettömästi ja sillä on saatu tälle laudalle ekat toimivat kokeilut ladattua.

Viime kerralla Ubuntussa oli hämärä käännösongelma jok on nyt ratkennut. Syynä on se, että jostain syystä 64-bittinen Linux ei suostu ajamaan 32-bittisiä binäärejä (arm-gcc) ilman toimenpiteitä.
Allekirjoittaneen omassa 64b Ubuntussa homma lähti pelittämään lataamalla seuraavat 32b tukipaketit:

$ sudo dpkg --add-architecture i386
$ sudo apt-get update
$ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386

Operaatioon saattaa liittyä riskejä, joten mikäli koneessa on jotain tärkeää niin se kannattaa varmuuskopioda alta pois. Allekirjoittaneella homma toimi kuitenkin moitteettomasti (joskaan koneella ei koskaan käännetä mitään muuta kuin cross-ARMia)


#6

voiko tähän analogia elektroniikka kurssille vielä tulla? mitä siihen tarvitaan?..minulla tietokoneena vain vanha mac, jonka kaikki ohjelmat vanhentuneet…pääsisin marraskuussa.


#7

Hei Sirpa,

Olet tottakai tervetullut osallistumaan.
Hacklabin maksuttomille kursseille (tämä on sellainen) on täysin vapaa
pääsy ilman mitään velvoitteita. Erityisesti ei ole velvoitetta osallistua
jokaiselle luennolle vaan aivan oman halun ja aikataulun mukaan.
Voit siis tulla käymään ja rauhassa päättää onko tämä sinulle kiinnostava
kurssi.

Kevätkaudella kurssilla käytiin läpi operaatiovahvistimen toiminnan
perusteet ja tärkeimmät vahvistin-. ja signaalinkäsittelykytkennät. Näiden
päälle rakennettiin yksinkertainen analoginen moottorin ohjaus
PID-säätimellä.
Nyt ollaan ottamassa haltuun STM32-mikroprosessoria jolla on tarkoitus
jatkaa digitaalista signaalinkäsittelyä ja siihen liittyvää analogisten
mittasignaalien normalisointia ja analogisten ohjaussignaalien
muodostamista ja muokkaamista. Eli jonkin aikaa kurssi on aika
“digitaalisessa” vaiheessa, mutta jahka se on tehty niin siirrytään
hyödyntämään sitä ja palataan analogisiin piireihin.
Kurssille ollaan valittu ST:n Nucleo F303-protolauta harjoittelukohteeksi,
ja myöhemmin on tarkoitus toteuttaa sen pohjalta omakin protopiirilevy
jolle kasataan enemmän kurssilla kiinnostavaa analogielektroniikkaa.

Kurssilla ei ole tiukasti rajattua sisältöä joten ehkä voisi puhua myös
jonkinlaisesta opintopiiristä jonka sisältöön on mahdollista vaikuttaa.
Näillä kumminkin ollaan menossa tällä hetkellä ja aiheesta kiinnostuneet
toivotetaan tervetulleiksi. Jos tuo STM32 pelottaa niin siihen on
mahdollista saada tukiopetusta käyntiinlähtöä vauhdittamaan. Kaikki
tarvittavat työkalut ovat ilmaisia ja toimivat myös Macissa, lukuunotamatta
Nucleo-protolautaa joka on maksullinen komponentti (kustannus 13,5€ eli ei
paha). Lautoja on tällä hetkellä yksi vapaana.

Seuraava kurssi-istunto on tänään klo 18 ja jatkuu viikoittain Ke samaan
aikaan.

-Martti

2016-10-12 10:26 GMT+03:00 Sirpa Jokinen <notifications@discourse.hacklab.fi


#8

kiitos perusteellisesta infosta! voisin tulla 2.11 muiden juttujen takia en
pääse ennen.
voin ostaa sen laudan ja siis tuon mun vanhan mackin mukana sitten.
Jos sulla on aikaa antaa tukiopetusta vaikka ennen kurssia, ottaisin
sellaistakin kiitollisena vastaan.

rakentelen soittimia itsekseni, mutta olen huomannut että jos tekee
muutakin analogista elektroniikkaa
siitä saa uusia tietoja jotka hyödyttää soitintenkin tekemisessä ja kiva
on tehdä muiden kanssa.

terv. Sirpa J.

  1. lokakuuta 2016 klo 11.31 Martti Paalanen <
    notifications@discourse.hacklab.fi> kirjoitti:

#9

Moi
Mulla on jäänyt parikertaa väliin ja sen huomaa…

Ongelmana on debuggaus. Ei oikein onnistu Eclipsen virittäminen.
Kirjan kappaleen 5.1.3 Configuring Eclipse mukaan olen koittanut toimia, mutta
RUN
-> external tools
-> externals tools configuration
ja sieltä MAIN välilehden asetukset kohdalleen
APPLY ja RUN nappuloiden painallus.
CONSOLE ikkunaan tulee punaisella tekstiä joissa yksi rivi on
Error: libusb_open() failed with LIBUSB_ERROR_NOT_FOUND

Ei auta vaikka tekisi kirjan sivulla 150 olevan harmaan laatikon Common OpenOCD Issues on the Windows Platform ohjeen mukaisen Zadig driverien päivityksen.

Tarkemmin sanittuna toi Zadig ohjelma kertoo, ettei päivitys onnistu ja jää jumiin.
Netistä bongattua asiaa sivuava tuore ketju.
http://www.carminenoviello.com/2015/01/07/setting-gcceclipse-toolchain-stm32nucleo-part-2/

Kysymys:
Saatiinko läbillä debuggaus haltuun edellisillä kerroilla?
Tarvitaanko jotain “säätämistä”


#10

Kannattaisikohan mun odottaa , että ‘kurssi siirtyy analogiseen
vaiheeseen’, kun mun kannettavassa macissa niin huonosti toimii mikään
ohjelma ja siihen voi asentaa vain kaikkein vanhimpia versioita
niistä…vai oisko mun parasta tulla tänään, että voin aloittaa
rakentamaan sitä F303 kun ilman sitä ei voi jatkaa?

Sirpa

  1. lokakuuta 2016 klo 11.31 Martti Paalanen <
    notifications@discourse.hacklab.fi> kirjoitti:

#11

Meillä on pientä haastetta ohjelmointiympäristön pystytyksessä ja joudutaan sitä debuggaamaan kuten edellisestä postauksesta huomaat. Ehkä tänään ei ole kaikkein hedelmällisin hetki aloittaa kun joutuisit vaan katselemaan vierestä aika kuivaa vianhakua. Itselläni on nimittäin vähän tuota samaa ongelmaa ja voi olla että menee hetki ennen kuin saadaan homma taas etenemään.
Mutta palataan ensi keskiviikon osalta asiaan, eiköhän nämä haasteet ole siihen mennessä voitettu ja voidaan jatkaa normaalisti.


#12

Eilinen CubeMX:n tuottama sorsabugi on tosiaan tunnettu bugi joka on raportoitu ST:n supportille. Varsinaista workaroundia ei ole, vaan koodi on pakko puukottaa käsin kuntoon., Tein sen minkä jälkeen käännös tietenkin onnistuu, joten siltä osin päästään ensi kerralla eteenpäin.


#13

Ensi kerralla jatketaan agendalla eteenpäin kun työkaluihin liittyvät haasteet on saatu ainakin suurimmaksi osaksi selvitettyä. Tässä siis vielä lyhyt yhteenveto siitä, miten on ajateltu jatkaa:
Vaikka “Mastering STM32” -kirjan kirjoittaja on tehnyt ansiokasta työtä rakentaessaan toolchainin niin silti käytetään ST:n omaa SW4STM32 työkalusettiä. Nämä ovat pitkälle identtisiä mutta SW4STM32 on eräiltä osin selvästi yksinkertaisempi käyttää.

###Tässä vielä pikaohjeet työkalusetin asennukseen:

SW4STM32 on ladattavissa Windowsille, Macille ja Ubuntulle OpenSTM32-verkkosivulta. Vaatii rekisteröitymisen mutta on ilmainen.

  • http://www.openstm32.org/HomePage
  • Vasemman laidan menusta “System Workbench for STM32”
    Nyt aukeaa sisällysluettelo jossa on mm. kohta “Downloading…” josta voi ladata itselleen sopivan version käännösympäristöstä. Täällä on myös peruskäyttöohjeet mm. uuden projektin luonnista, CubeMX:llä tehdyn projektin importtaamisesta jne jne.

Edellisen lisäksi pitää olla asennettuna ST-Link/v2 ohjelmointi/debuggerisofta. Nämä ovat samat kuin kirjan työkalusetissä, joten jos olet jo asentanut kaikki niin ei tarvitse toista kertaa.
Asennusmediat täällä (vaatii rekisteröitymisen ST:n sivuille):
http://www.st.com/content/st_com/en/search.html#q=ST-Link/v2-t=keywords-page=1
Lataa ja asenna siis ainakin STSW-LINK004 ja 007, sekä WIndows-käyttäjät myös 009 (USB-driver).

Lopuksi pitää olla asennettuna STCubeMX -HAL konfiguraattori. Me tarvitsemme uusimman version (4.17.0). Varmista versio CubeMX:n valikosta Help/About. Tarvittaessa lataa ja asenna se täältä.

Kurssilla työstetty esimerkkiprojekti on napattu taannoisesta ST:n Ecosystem Workshop-materiaalista sitä hieman modaten. Lyhyesti toiminnallisuus ja sitä tukeva HAL-konfiguraatio on seuraava:

  1. ADC 1
  • kanava 6 on aktivoitu single-ended -moodiin ja kytketty oletuspinniin (PC0)

  • Muunnin tekee 12-bitin muunnoksia yksi kerrallaan ulkoisesta liipaisusta.Ulkoinen liipaisu on kytketty Timer 2:n Capture Compare 2 Event:iin

  • ADC 1 on kytketty DMA-ohjaimen 1 kanavaan 1. Tulokset siirretään muuntimelta muistiin suoralla muistiosoituksella aina ADC 1:n signaloidessa muunnoksen valmistumista.

  • Timer 2

  • Kellolähde on kytketty Advanced Peripheral Bus 1:n timer-haaraan ja vastaanottaa sieltä 72 MHz kellon

  • Kellon esijakajaksi on asetettu 72 jolloin laskuri laskee ylöspäin 1 MHz taajuudella

  • Laskurin periodiksi (autoreload) on määritelty 1000 jolloin yksi laskentasykli kestää 1000 us = 1 ms

  • Laskurin liipaisulähdöksi on määritelty Update Event

  • Kanava 1 on määritelty PWM-lähdöksi jonka pulssitusarvo on 500 kellojaksoa (eli pulssisuhteeksi tulee 50%). Tämä kanava on kytketty prosessorin pinniin PA0 josta pulssitusta ja samalla timerin toimintaa voi havainnoida skoopilla.

  • Kanava 2 toimii moodissa “Output Compare No Output” eli vertailu ilman fyysistä lähdönohjausta. Verrattava lukuarvo on 200 eli aina Timer2-laskurin saavuttaessa tuon arvon, kanava 2 generoi Eventin. Tämä event on kytketty AD-muuntimen 1 liipaisuun ja käynnistää uuden muunnoksen.

  • DMA 1

  • DMA-ohjaimen 1 kanavqa 1 on kytketty A/D-muuntimeen ja lukee sen muunnosrekisteristä tuloksen sekä siirtää sen muistiin jatkokäsittelyä varten. Muisti on määritelty ympyräpuskuriksi, mutta tarkemmat muistiosoitteet annetaan vasta sovelluskoodissa kun muuttujannimet ja puskurin pituus (RAM-osoitteet) on tiedossa.

  • Vastaava määritys on tehty myös koskien D/A-muuntimen kanavaa 1 jolle ladataan arvoja muistipuskurista (ei sama puskuri kuin edellä).

Projektin main-funktio jossa varsinainen sovelluskoodi:

int main(void)
{

  /* USER CODE BEGIN 1 */
uint16_t cnt = 0;
volatile uint16_t bufsum, bufavg;
volatile uint8_t flag;

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_DAC1_Init();
  MX_TIM2_Init();

  /* USER CODE BEGIN 2 */
  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
  HAL_Delay(500);

  HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,&bufavg,sizeof(uint16_t),DAC_ALIGN_12B_R);
  HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adcbuf,ADCBUFSIZE);
  HAL_TIM_OC_Start(&htim2,TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  for (cnt = 0; cnt < ADCBUFSIZE; cnt++) {
	  adcbuf[cnt] = 0;
  }
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
	  bufsum = 0;
	  for (cnt = 0; cnt < ADCBUFSIZE; cnt++) {
		  bufsum += adcbuf[cnt];
	  }
	  bufavg = bufsum / ADCBUFSIZE;
	  HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
	  HAL_Delay(50);
  }
  /* USER CODE END 3 */

}

Esimerkkiprojektin HAL-määritys CubeMX:ssä: https://www.dropbox.com/s/45b1qct40fy5rmk/F303_ADC_DAC2.ioc?dl=0

Olisi hyvä jos jokainen ehtisi kokeilemaan esimerkkiprojektin kääntämistä omassa koneessa. Tapahtuu seuraavasti:
-Luo sopivaan paikkaan kansio johon lataat ylläolevan Cube-projektitiedoston.
-Käynnistä CubeMX ja avaa projektitiedosto (File/Load Project)
-Varmista että projektin asetukset ovat halutut (Project/Settings):

Project:

  • Toolchain/IDE: SW4STM32
  • Generate Under Root [x]

Code Generator:

  • Copy only the necessary library files (o)
    Keep User Code when regenerating [x]
    Delete previously generated files when not re-generated [x]

-Generoi HAL (Project/Generate Code) Valitse paikka johon koodi generoidaan.

-Käynnistä Eclipse (ST4STM32) ja heitä mahdollinen Welcome-läystäke pois edestä.
-Importtaa Cubella generoitu HAL-koodi uudeksi projektiksi. Tämä on selitetty hyvin OpenSTM32-sivustolla kohdassa Importing a STCubeMX generated project

Käännös tuottaa virheen johtuen aiemmin mainitusta CubeMX:n bugista. Korjaus on yksinkertainen: etsi kohta jossa virheellistä makrodefineä käytetään ja poista if (…) rakenne koodista niin, että makroa kutsutan ilman mitään vertailuja. Löydät oikean kohdan kääntäjän virheilmoituksesta (toka viite joka johtaa paikkaan jossa makroa käytetään).
Korjauksen jälkeen käännöksen pitää mennä läpi ja koodia voi ajaa debuggerissa.
Jatketaan tästä ensi kerralla.


#14

Tuli kokeiltua tuota NUCLEO-F303RE lautaa. Eilen labilla PA4 (DAC1_out) portista tuli nättiä siniaaltoa. Tänään en saanut tulemaan kovinkaan nättia aaltoa. Jostain syystä se leikkaa puolet aallosta pois. Olisinkohan onnistunut käräyttämään NUCLEO laudan??

Kuvassa: Sininen aalto sisään PC0 (ADC_in)
Keltainen aalto ulos PA4 (DAC1_out)


#15

Jos noi kuvan vasemman laidan pikku nuolet osoittaa skoopin 0-jännitetason,
niin sulla on tulosignaali bipolaarinen, eli menee saman verean nollan ali
ja yli. Sitä ei AD-muunnin pysty käsittelemään vaan hyytyy nollan kohdalle.
Koeta kehittää vähän offsettia tulosignaaliin niin, että se pysyy kokonaan
positiivisena.
Martti


#16

Hyvä huomio. Käytössäni on sellainen halpis Vellemanin funktiogeneraattori, josta ei saa offsettia säädettyä. Sain kuitenkin offsetin aikaiseksi yhtä oparia (lm324) käyttämällä. Nyt rupesi NUCLEO lauta pelittää. Kiitos!


#17

Keskiviikkona 30.11. saatiin viimein Nucleo F303 ajamaan digitaalisuotoa reaaliajassa. Suotimena oli klassinen tyypin 1 FIR-alipäästö jonka kerroinjono oli laskettu Iowa Hillsin FIR-suotimen suunnittelusovelluksella (kiitos ojousimalle linkkivinkistä) http://iowahills.com/
Suotimen kulmataajuus oli asetettu 0,1 x näytteenottotaajuudelle ja kun näytteitä napsittiin 1kHz taajuudella niin kulman piti osua 100 Hz kohdalle. Ihan napakymppiin ei osuttu ja säädin myöhemmin koodia nostamalla näytteenoton 10kHz ja generoimalla kerroinjonon uudelleen.
Ohessa mittaustuloksia modatusta toteutuksesta:

Ekassa kuvassa havainnollistettu suotimen laskennan kestoaikaa. A/D-muunnin tuottaa näytteitä 100 us välein ja jokaisen keskeytyksen alussa asetetaan GPIO-pinni 1-tilaan ja lopussa pudotetaan alas. Skoopilla näkee sitten suoraan kauanko filtterin ajo kestää ja paljonko aikaa jää muuhun iteraatioiden välillä. Ohessa lopputulos. Huomaa kuvan alareunan mitta-arvot.

Tokassa kuvassa haettu ensimmäinen -3dB piste taajuusvasteesta ja se osuu noin 196 Hz kohdalle. Se nyt ei ihan vastaa suunniteltua 1 kHz taajuutta joten jossain joku mättää lievästi.

Ensimmäinen nollakohta osuu 306 Hz kohdalle (huomaa kanavan 2 skaalaus vaihdettu 200mV -> 10 mV per jako-osa). Skoopin maajohdon sieppaamat I/O:n kytkentäpiikit näkyvät selvästi ja tekevät amplitudin automaattisen mittauksen merkityksettömäksi. Silmällä arvioiden lähtöamplitudi on n. 10mVpp tai vähän yli.

Seuraava maksimi osuu n.526 Hz taajuudelle ja senkin amplitudi on n. -3dB mikä ei nyt oikein natsaa oppikirjan mukaisten suotimien toistokäyrän kulkuun. Eli koodin osalta jää vielä kotiläksyä vaikka rakenteen toki pitäisi olla oikein…

Tässä vielä asiasta kiinnostuneille käytetyn suotimen koodi ja kerroinjono:

// Low pass filter with 16 taps. Corner @ 0.1 x sampling
 #define NUM_TAPS 16
float LP1kcoeffs[NUM_TAPS] = {
-0.028156774387097804,
-0.041622036924535436,
-0.035698168970811295,
-0.003403302593968809,
 0.053154605145074939,
 0.121878244119803664,
 0.184063001487414751,
 0.220978933758780416,
 0.220978933758780416,
 0.184063001487414751,
 0.121878244119803664,
 0.053154605145074939,
-0.003403302593968809,
-0.035698168970811295,
-0.041622036924535436,
-0.028156774387097804 };

typedef struct realTimeFIR_t {
	uint16_t *signalBuffer;
	uint16_t signalLength;
	uint16_t signalIndex;
	float *FIRCoeff;
	uint16_t coeffLength;
} realTimeFIR_t;

void realTimeFIR_Init(realTimeFIR_t *FIR, uint16_t *buf, uint16_t buflen, float *coeff, uint16_t coefflen) {
	FIR->signalBuffer = buf;
	FIR->signalLength = buflen;
	FIR->signalIndex = 0;
	FIR->FIRCoeff = coeff;
	FIR->coeffLength = coefflen;
}

uint16_t realTimeFIR(uint16_t signal, realTimeFIR_t *FIR) {
	uint16_t convCnt;
	volatile uint16_t filteredSignal;
	*(FIR->signalBuffer + FIR->signalIndex++) = signal;
	if (FIR->signalIndex >= FIR->signalLength ) FIR->signalIndex =0;
	filteredSignal = 0;
	for ( convCnt = 0; convCnt < FIR->coeffLength; convCnt++ ) {
		filteredSignal += *(FIR->signalBuffer + FIR->signalIndex++) * *(FIR->FIRCoeff + (FIR->coeffLength - convCnt));
		if (FIR->signalIndex >= FIR->signalLength ) FIR->signalIndex =0;
	}

	return filteredSignal;
}

// Suodattimen signaalipuskuri johon kerätään A/D-muunnettu signaali
 #define ADCBUFSIZE	32
volatile uint16_t adcbuf[ADCBUFSIZE];

realTimeFIR_t LPFilter1;	// suodattimen koontistruktuuri jossa koottu signaalipuskuri ja kerroinjono sekä osoittimet niihin

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_DAC1_Init();
  MX_TIM2_Init();

  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);

  HAL_ADC_Start_IT(&hadc1);

  HAL_TIM_OC_Start(&htim2,TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);

  // Alustetaan suodattimen puskurit ja osoittimet
  realTimeFIR_Init( &LPFilter1, &adcbuf, ADCBUFSIZE, &LP1kcoeffs, NUM_TAPS);

   while (1)
  {
  }

}

/ A/D-muunnoksen keskeytyspalveluohjelman callback jossa ajetaan suodatin
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
  volatile uint16_t adcvalue;
  // -Haetaan A/D-muuntimelta viimeisin muunnosarvo
  // -Suodatetaan muunnosjono
  // -viedään suodatuksen tulos D/A-muuntimelle uudeksi lähtöarvoksi
  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);
  HAL_DAC_SetValue( &hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, realTimeFIR( HAL_ADC_GetValue( AdcHandle ), &LPFilter1 ) );
  HAL_DAC_Start(  &hdac1, DAC_CHANNEL_1 );
  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
}

#18

… ja virhekin löytyi koodista. Edellisen viestin toteutuksella signaalipuskurin pitää olla saman mittainen kuin kerroinjonon, muuten laskennassa käytetyt indeksit ajautuvat pois tahdista. Tämä ei siis ole mikään suotimen edellytys, vaan yksinkertaisesti tuon kirjoittamani toteutuksen heikkous. Mutta ei muuteta sitä nyt tähän hätään - säädin vain signaalipuskurin samaksi 16 näytteen pituiseksi kuin kerroinjonokin.

Nyt suodatin käyttäytyy kauniisti ja antaa varsin hyvin samat lukemat kuin suunnitteluohjelmakin. Ohessa vielä Iowa Hillsin FIR-designerin käsitys asiasta:

Skoopilla mitattuna suotimen vahvistus kulmataajuudella 1kHz on -3,25 dB mikä vastaa oikein hyvin teoreettista arvoa (-3,0 dB). Myös kampa rajataajuuden yläpuolella osuu hienosti ennustetuihin arvoihin muutaman desibelin tarkkuudella.


#19

Tässä vielä vaihtoehtoinen, hieman tehokkaampi suodatintoteutus. Sama alipäästö ja suodattimen kerroinjono voidaan laskea samoilla kaavoilla. Tässä kuitenkin hyödynnetään sitä seikkaa, että kun kerroinjonon pituus on parillinen, lukuarvot ovat peilisymmetrisiä jonon puolivälin suhteen. Tämä tarjoaa helpon optimointikeinon eli folded filterin. Sen sijaan, että jokainen näyte ensin kerrotaan kertoimella ja sitten summataan, voidaankin jonon järjestyksessä ensimmäinen ja viimeinen näyte summata ensin ja sen jälkeen kertoa kumman tahansa kertoimella (koska kerrointen lukuarvo on sama). Sen jälkeen summataan edellinen ja viimeistä edellinen näyte jne jne. Lopputuloksena saadaa selvästi laskentatehokkaampi suodin koska kertolaskujen ja silmukkaiteraatioiden määrä puolittuu.

int16_t realTimeFIR_folded(int16_t signal, realTimeFIR_t *FIR) {
	uint16_t convCnt;
	volatile int16_t tempSum, filteredSignal;
	int16_t tempIndex1, tempIndex2;

	tempIndex1 = FIR->signalIndex;
	if (tempIndex1 == 0) tempIndex2 = FIR->signalLength -1;
	else tempIndex2 = FIR->signalIndex -1;

	*(FIR->signalBuffer + FIR->signalIndex++) = signal;
	if (FIR->signalIndex >= FIR->signalLength ) FIR->signalIndex =0;
	filteredSignal = 0;

	for ( convCnt = 0; convCnt < FIR->coeffLength/2; convCnt++ ) {
		tempSum = *(FIR->signalBuffer + tempIndex1++) + *(FIR->signalBuffer + tempIndex2--);
		filteredSignal += tempSum * *(FIR->FIRCoeff + convCnt);
		if (tempIndex1 >= FIR->signalLength ) tempIndex1 =0;
		if (tempIndex2 <0 ) tempIndex2 = FIR->signalLength -1;
	}

	return filteredSignal;
}

Annetuilla kertoimilla ylläoleva koodi kuormittaa STM32F303-prosessoria 15,6% kokonaiskapasiteetista, kun suora laskenta kuormitti yli 20%, eli ero on selvästi jälkimmäisen eduksi.


#20

14.12. keskiviikkoistunnossa siirryttiin ihmettelemään moottorinohjausta ja siinä esiin tulevia juttuja. Aloitettiin kertaamalla harjallisen DC-moottorin toiminnan perusteet ja käytiin läpi kuinka semmoisen ohjaus onnistuu. Ohessa istunnon aikana läpikäyty matsku:

DC-moottori

DC-moottorin ohjauksesta

Nopeuden mittaus vasta-SMV:n avulla