Visserslatijn

20 januari 2019

Upgrade ST-Link V1 to V2

Filed under: Geen categorie — visserslatijn @ 18:12

Some time ago I read that Atollic Truestudio V9.2 no longer supports flashing and debugging STM32 micro controllers with a ST-Link V1. I didn’t bother me much as I have several ST-Link V2 devices, on Nucleo boards and stand alone.

Except for one, an older Discovery board with a STM32F100RB. Now, I could still flash and program it, just disable the on-board ST-Link and use an external one. But that’s not as convenient as having one on the board itself. And unfortunately the ST-Link-Utility does not want to upgrade a V1 to V2.

So I took a look at the schematics of a Nucleo and the Discovery. There isn’t all that much difference in the ST-Link V1 on the Discovery and the V2 on a Nucleo. The 1k5 resistor to D+ is switched by a transistor on a Nucleo whereas it is directly connected to 3v3 on a Discovery.

usb_nucleo

Nucleo

usb_discovery

Discovery

Changing that would be a bit difficult for a guy, used to through-hole-components, so I chose to ignore that.

Other differences are, of course the micro-controller itself, on a Discovery it is a STM32F103C8 where on a Nucleo is is a STM32F103RB with twice as much flash memory. That was another thing I ignored as I already knew that V2 works on a C8 (but without the virtual COM-ports). On a Nucleo RCC_MCO (8 MHz) is used as an external clock for the micro-controller you want to program, on a Discovery there is no option to do that, so that was easy. There was just one other thing that differs. A connection from PB12 to PB14 with a 100 ohm resistor (R24). I guessed it would work with 0 ohm just as well.

nucleo_st_link_v2

Nucleo, with serial support and the connection PB12 – PB14

And of course the thing had to be reflashed with the ST-Link V2 firmware. Now, as you could upgrade the V1 with several updates over the years it could not be locked completely. But the thing would be read-protected via the Option Bytes. When you remove this protection you end up with a blank micro-controller. That was fine by me, as a ST-Link V1 it didn’t have any value anyways.

So there had to be a connection between PB12 and PB14 and a connection between another ST–Link  and the STM32F103C8 on the Discovery board. This called for some pretty fine soldering.

discovery_2

discovery_3

And I needed the firmware. As had been experimenting with flashing firmware to a “blue-pill” I already had some. I do not remember where it actually came from but I remember reading things on these sites:

Reverse engineering ST-Link/V2 firmware

http://www.taylorkillian.com/2013/01/retrieving-st-linkv2-firmware-from.html

I forgot to make screenshots while changing the Option Bytes, flashing  the bootloader and upgrading with the ST-Link_Utility but I’ll add the pictures I made later anyway.

option_bytes

the uC will be empty after disabling the Read Out Protection

v1_upgraded_v2

There was a small list of options, I chose “STM32 – only”

discovery_st_link_v2

Succes! I can now use the Discovery board again to flash and debug the STM32F100RB. You cannot see it, but in this last picture the LED is flashing.

discovery_4

I wonder how long it will last before ST decides to stop supporting the ST-Link V2, as there is a V3 on the market as I write this. But for now, I can use this board again without having to connect an external ST-Link.

5 mei 2016

GPS disciplined OCXO

Filed under: Geen categorie — visserslatijn @ 21:17

Een hele tijd geleden heb ik eens mee gedaan met zo’n crowdfunding actie voor een GPS dingetje (NavSpark GL). Daar heb ik eigenlijk nooit iets mee gedaan. Nou was ik een poosje bezig met een OCXO die ik heb gecalibreerd met mijn Rubidium standaard, wat overigens heel goed werkt.

In die tijd kwam ik zo nu en dan zelfbouw projecten tegen die de OCXO continu afstemden met behulp van een GPS. Dat wilde ik ook eens proberen. Ik heb het ding weer opgezocht en aangesloten. Daar komt een hele mooie puls uit van precies 1Hz. Heel precies volgens de docs, maximale jitter iets van 10nS.

navspark_gl

Behalve die 1puls per seconde (1PPS) komt er natuurlijk ook GPS data uit in de vorm van een 115200 baud stream van NMEA strings. Daar doe ik nog weinig mee, ik gebruik het om de tijd in UTC, de datum, het aantal zichtbare satellieten op een scherm te zetten. Ik was eerst van plan om van die data het aantal satellieten te gebruiken als signaal dat je de output van de oscillator mag vertrouwen. Dat bleek later niet nodig. De NMEA data wordt dus niet echt gebruikt. Al heb ik er wel een heel nauwkeurige klok erbij.

De OCXO die ik heb liggen levert een merkwaardige frequentie van 38,880,000 Hz. Dat is volgens Wikipeadia een frequentie die door de telecommunicatie maatschappijen werd gebruikt voor hun onderlinge communicatie. Niet interessant genoeg om daar veel tijd aan te besteden. En omdat het zo’n “onhandige”  frequentie is zijn ze lekker goedkoop via ebay te koop.

ocxo

Pas laat kwam ik er achter dat je deze dingen over een erg groot bereik kunt tunen, hoeveel precies weet ik niet meer maar het zijn honderden Herz boven en onder die 38.88MHz. Waarom zoveel is me een raadsel en het leverde me ook problemen op, waarover later meer. Je stemt de oscillator af met een regelspanning op de EFC ingang.

De 1PPS puls gaat een 74HC390 (dual decade counter) in die ik zo gebruik dat er een signaal van 8 seconden laag en 2 seconden hoog uit komt.

Dat signaal is de CLK-enable van een teller die als ingangsignaal  de OCXO heeft en dus 8 seconden lang 38,880,000 pulsen telt. Dat zijn er 311,040,000.

De microcontroller is een ATMEGA8 die niet in staat is om 38MHz signalen te verwerken, daarnaast wilde ik de counters van de controller helemaal niet gebruiken omdat het in en uitschakelen altijd gesynchroniseerd wordt met de clock van de microcontroller, ook als de counter een externe clock heeft. Dat zou al snel enkele microseconden fout opleveren.

De counter is dus ook in hardware uitgevoerd. Eerst wilde ik een enkele 74HC590 (8 bit binairy counter) gebruiken. Dat klinkt op het eerste gezicht wat vreemd, die kan immers maar van 0 tot 255 tellen en er komen meer dan 300 miljoen pulsen voorbij. Maar het zijn er altijd ongeveer evenveel. Zouden het er precies 311,040,000 zijn dan is de teller 1,215,000 keer rond gegaan en staat deze dus op 0. Als het er een paar meer waren komt de teller iets hoger te staan en waren het iets minder dan staat de teller nog voor de 255. Met een signed integer van 8 bits krijg je dan mooi een getal dat het tekort of overschot aan pulsen voorstelt.

Helaas werkte dat niet goed. En dat komt doordat de OCXO, zoals ik al eerder zei, over een groot bereik getuned kan worden. De afwijking van gewenste aantal pulsen kan makkelijk meer dan plus of min 128 worden. De oplossing was dus twee tellers te gebruiken, een extra investering van 25 eurocent. Dat is geen probleem, ik maak tenslotte maar een (1) apparaat, geen hele serie. Maar dit leverde een ander, groter, probleem voor me op. Zie hier het schema:

20160505_174118

De  1PPS die door tien gedeeld wordt en een duty cycle van 80% heeft enabled de clock van de tellers en gaat ook naar de INT0 ingang van de ATMEGA8. Zodra de tellers stil staan nadat ze 8 seconden hebben geteld wordt er een interrupt gegenereerd en zijn er 2 seconden de tijd om de data te verwerken. De tellers zetten de data op de uitgangen met behulp van RCK en kunnen daarna gereset worden. Dan leest de microcontroller de tellerstanden en gaat daarmee aan het werk. Nu was het plan om met een enkele 8 bit counter te werken, er bleven dan voldoende GPIO poorten over voor andere dingen, zoals een LCD schermpje. Dat ging dus niet door, alle GPIO poorten zijn in gebruik. Er moet zelfs gebruik gemaakt worden van de interne 8MHz rc oscillator, zo weinig poorten waren er over. Eigenlijk zou ik over moeten stappen op een andere controller… Maar goed, nu mag de tellerstand wel 32000 te hoog of te laag zijn. Zo groot is het tunings bereik van de OCXO niet. Jammer is wel dat als de juiste frequentie bereikt is de teller niet mooi op nul uitkomt maar op 6144.

Het afstemmen van de OCXO gaat zoals gezegt met een spanning tussen de 0 en 5 volt, na meten bleek de oscillator zo rond de 1.25v op de juiste frequentie te staan. Dus met een regelbare spanning tussen de 0 en 2.5v is deze mooi af te stemmen. Hiervoor wordt TIMER1 gebruikt die een PWM signaal maakt  in 32768 stapjes, dat leek me wel nauwkeurig genoeg, stapjes van 76 uV. En de PWM frequentie is zo ook nog hoog genoeg (244 Hz) De 47u condensator is later vervangen door eentje van 220u wat een lowpass filter met een cutoff van 0.05Hz geeft. Dit gaat direct de OCXO in, die is hoogohmig genoeg.

Hieronder het programma voor de aansturing van de OCXO,  in een volgende post de uitleg.

#define F_CPU 8000000UL
#define UART_BAUD_RATE 9600
#define CLR_74HC590 PB0
#define RCK_74HC590 PB2
#define REMAINDER 6144
#define PWM_START 0x41cd
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include "uart.h"

volatile int16_t old_pwm[8];

int main(void)
{
 OSCCAL = 0x9c;
 DDRB = 0x07; //PB0,1,2 output
 DDRC = 0x00; //all input
 DDRD = 0x00; //all input (uart does its own init)

 TCCR1A |= (1 << COM1A1) | (1 << COM1A0); //SET OC1A on Compare Match, CLEAR OC1A at BOTTOM
 TCCR1A |= (1 << WGM11); //fast pwm, top = ICR1
 TCCR1B |= (1 << WGM13) | (1 << WGM12); //fast pwm
 TCCR1B |= (1 << CS10); //prescaler = 1
 ICR1 = 0x7FFF;
 OCR1A = PWM_START; //pwm starting value
 
 MCUCR |= (1 << ISC01) | (1 << ISC00); //INT0 on rising edge
 GIMSK |= (1 << INT0); //interrupt INT0 enabled
 
 uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
 uart_flush();
 
 for (uint8_t x=0; x<8; x++) //fill array with wanted settings
 {
   old_pwm[x] = PWM_START;
 }
 
 sei();
 
 while (1)
 {
   //nothing, all work is done in the interrupt routine
 }
}

ISR(INT0_vect) //once every 10 seconds, after 8 seconds of counting
{ //we have two seconds here, so no hurry
 uint16_t stepsize = 32;
 uint8_t freq_low, freq_high;
 int16_t frequency = REMAINDER;
 static uint32_t pwm_value = PWM_START;
 static uint8_t x = 0;
 uint8_t y = 0;
 char buffer[7];

 PORTB |= (1 << RCK_74HC590);
 _delay_ms(1);
 PORTB &= ~(1 << RCK_74HC590);
 _delay_ms(1);
 PORTB &= ~(1 << CLR_74HC590);
 _delay_ms(1);
 PORTB |= (1 << CLR_74HC590);
 _delay_ms(1);
 
 freq_low = (PINC & 0x3F) | (PIND & 0xC0);
 freq_high = ((PINB & 0xF8) >> 3) | ((PIND & 0x38) << 2);
 frequency = (freq_high << 8) | freq_low;
 
 if ((frequency - REMAINDER) > 2)
 {
 if ((frequency - REMAINDER) >= 15) stepsize = 256;
 if ((frequency - REMAINDER) < 15) stepsize = 16;
 if ((frequency - REMAINDER) < 10) stepsize = 4;
 if ((frequency - REMAINDER) < 5) stepsize = 1;
 if ((frequency - REMAINDER) < 3) stepsize = 0;
 pwm_value += stepsize;
 }
 
 if ((REMAINDER - frequency) > 2)
 {
 if ((REMAINDER - frequency) >= 15) stepsize = 256;
 if ((REMAINDER - frequency) < 15) stepsize = 16;
 if ((REMAINDER - frequency) < 10) stepsize = 4;
 if ((REMAINDER - frequency) < 5) stepsize = 1;
 if ((REMAINDER - frequency) < 3) stepsize = 0;
 pwm_value -= stepsize;
 }
 
 uart_puts("FRQ");
 ltoa(frequency, buffer, 10);
 uart_puts(buffer);
 uart_puts("\n\r");
 
 uart_puts("PWM");
 ltoa(pwm_value, buffer, 16);
 uart_puts(buffer);
 uart_puts("\n\r");
 
 old_pwm[x] = pwm_value;
 if (x < 7) x++;
 else x = 0;
 
 pwm_value = 0;
 for (y=0; y<8; y++)
 {
   pwm_value += old_pwm[y];
 }
 pwm_value = pwm_value / 8;
 
 OCR1A = (uint16_t) pwm_value;
 
 uart_flush();
}

 

 

18 januari 2016

DIY_DDS

Filed under: Geen categorie — visserslatijn @ 20:56

Een signaal generator is altijd makkelijk om er bij te hebben. Dertig jaar geleden (… ik wordt oud) had ik er een gemaakt met een XR2206. Dezelfde ontwerpen vind je trouwens nu nog als bouwpakketje of als kant een klaar printje op diverse plaatsen. Zoek maar eens op Ebay.

Van dat zelfde Ebay had ik enige tijd geleden een AD9851 module gekocht en er ook een signaal generator mee gemaakt.

AD9851

Daar komt een mooie 1 Volt tt sinus uit van 1Hz tot 10MHz, ja hij kan hoger maar dat heb ik toch nooit nodig. Het ding heeft een klokfrequentie van 180MHz en als er per 2 klokcycli een sample van de sinus gemaakt wordt zijn er bij 10 MHz nog krap 9 over. Dat zal al geen mooie sinus meer zijn. Het apparaat is nog niet af, geen uitgangsbuffer, geen volume regeling en geen offset. De output is nu een sinus van 1 Volt tt met een vaste offset van 0.5 Volt. Niet altijd even practisch.

Maar het is leuker om zelf een signaal generator te maken, uiteraard ook met DDS. Die kan dan mooi die oude XR2206 vervangen. Het maken van een DDS blijkt verrassend eenvoudig, de slimmigheid zit ‘m in de phase-accumulator. Ik heb ‘m gemaakt met een ATMEGA328 op 16MHz. Met een timer laat ik 100.000 interrupts per seconde genereren die dus minder dan 160 klokcycli lang mogen duren. En dan blijft er niets over voor de rest van het programma, die de rotary encoder, een drukknopje en het LCD scherm nog moet aansturen.

Als eerste een R2R ladder netwerkje gemaakt met 1k en 2k weerstanden, vrij lage waarden maar het blijkt goed te werken. Omdat de phase-accumulator routine heel regelmatig moet functioneren laat ik de rotary encoder een eenmalige interrupt genereren die vervolgens een timer inschakeld voor het debouncen van de encoder (die zijn echt heel slecht). Na ongeveer een kwart seconde gaan alle verstorende interrupts weer uit en blijft alleen die van de phase-accumulator over. Je kan zien dat er kleine onregelmatigheden in het signaal zitten als je aan de knoppen zit, maar zolang je er van af blijft is het signaal mooi schoon. Al zitten er nog wel problemen aan het maken van signalen met een dds, waar ik later op terug zal komen. (TODO: jitter)

Zie hier de schema’s

DIY_DDS_GENERATOR

 

DIY_DDS_FILTER

 

Als microcontroler gebruik ik een Arduino Pro Mini die gewist is en direct vanuit Atmel Studio geprogrammeerd wordt het is dus geen Arduino meer. De software van Arduino maakt het ding veel te traag en ik mis de controle over de interrupts.

Hier de opbouw op een breadboard. (links de generator, rechts het filter)

DIY_DDS_01

Ik had flink problemen met het kiezen van de juiste poorten op de ATMEGA328. Het mooiste is natuurlijk als je de juiste waarde voor het volgende sample van de golfvorm in één keer op de uitgang kan zetten, maar helaas het mocht niet zo zijn. PORTB kon ik niet gebruiken omdat daar het kristal al op is aangesloten, en ik wilde natuurlijk wel kristal stabiliteit en een “hoge” klok frequentie. De interne klok is 8MHz en niet nauwkeurig en ook temperatuur afhankelijk. PORTC viel ook af omdat die maar zes bits breed is, PC6 is de RESET en PC7 werkt om een of andere reden ook niet. En helaas kon ik ook niet alle 8 bits van PORTD gebruiken omdat ik gebruik wil maken van de INT0 (externe) interrupt.

Er zat dus niets anders op dan de data in twee nibbles via PORTC en PORTD naar buiten te sturen. Achteraf blijkt dat geen probleem te zijn, de glitches zijn klein en zeer kort zodat het filter ze volledig verwijderd.  Hieronder de hele interrupt voor het genereren van de golfvormen:


//interrupt voor het genereren van de golfvorm, 100k / seconde
//omdat er erg weinig tijd tussen de interrupts zit geen andere dingen uitvoeren
//als er wel andere interrupts worden uitgevoerd is de golfvorm niet meer zuiver.
ISR(TIMER0_COMPA_vect)
{
static uint32_t counter = 0;
static uint8_t output = 0;
//step defines the frequency (42950 --> 1 Hz, 429500 --> 10Hz, etc.)
counter += stepsize;
switch (waveform)
{
case 1:
output = sine[counter >> 24];
break;
case 2:
output = triangle[counter >> 24];
break;
case 3:
output = sawtooth[counter >> 24];
break;
case 4:
output = squarewave[counter >> 24];
break;
case 5:
output = sinxdivx[counter >> 24];
break;
default:
break;
}
//door een tekort aan ports de output verdelen over PORTC en PORTD
//lower nibble van PORTD is ingangen, pull-up weerstanden ingeschakeld laten
PORTC = output & 0x0F;
PORTD = (output & 0xF0) | (0x0F);
}

Het gebruik van “switch – case” bleek sneller te werken dan een lijst van “if – else”. Weten we dat ook weer :-) En zoals je ziet, wordt er gebruik gemaakt van vijf lookup tabellen voor de verschillende golfvormen.

Om ervoor te zorgen dat bovenstaande routine niet gestoord wordt staat er maar één andere interrupt aan die een neergaande flank van de rotary encoder detecteert, de INT0 interrupt doet niets anders dan een andere timer-interrupt inschakelen die naar de stapjes van de encoder luistert, zichzelf na een kwart seconde uit zet en de externe interrupt (INT0) weer inschakelt.

Hier de output van de generator op 1 kHz, mooi he!

DIY_DDS_07

Helaas bleek het niet allemaal zo mooi te zijn. Ik heb er de tabellen voor een sinus, driehoek, zaagtand, blokgolf en iets dat sin(x)/x heet in geladen. De sinus, driehoek en sin(x)/x zijn goed tot redelijk, ook op de hoogste frequentie (10kHz). Maar de zaagtand en blokgolf hebben vreselijk last van jitter ook wel dds-phase-noise genoemd geloof ik. Na een poos er over zitten na denken kan het ook niet anders dan een rommelig signaal geven. Zeker bij de hogere frequenties wordt het aantal samples uit de lookuptabel erg klein, bij 10kHz nog maar 10 en het is natuurlijk heel goed mogelijk dat de ene keer een andere waarde uit de tabel wordt gekozen dna de volgende keer. Bij een blokgolf en een zaagtand is dat desastreus! Daar is er namelijk een moment dat de waarde van 0 naar 255 (en andersom) gaat. Als je die de ene keer 10uS eerder of 10 uS later treft heb je een flinke jitter te pakken. Bij een sinus gaat het allemaal veel geleidelijker zodat de jitter er in principe wel is maar nauwelijks zichtbaar is (en door het filter nog minder wordt?)

Dus maak ik nu een TTL blokgolf door de sinus door een schmitt trigger te halen. De blokgolf uit de DDS laat ik er wel in zitten, net als de zaagtand, ik heb toch geen idee wat ik er mee moet doen :-).

Nog een paar plaatjes:

Deze zijn allemaal gemaakt op 1 kHz, op de maximale frequentie van 9,999.99 Hz ziet het er allemaal wat minder mooi uit. En wat ik met die sin(x)/x moet weet ik ook nog steeds niet.

Wat heel goed gaat met een DDS is heel lage frequenties maken. Ik ben niet verder gegaan dan 10mHz (milli, niet Mega). Je moet er even geduld voor hebben maar dan heb je ook een mooie sinus op 0.01 Hz. Ook daarvan weet ik echt niet waar het ooit goed voor is.

DIY_DDS_14

En daarna moet het allemaal nog in een kastje, de oude XR2206 er uitgesloopt, de voor en achterkant verwisseld en een stukje pertinax  (jawel dat heb ik ook nog) op de achterkant gelijmd. En dan volgt het voor mij altijd erg moeilijke deel, een indeling maken voor het frontpaneel en er een beetje netjes gaten in maken.

Inmiddels zit er nog een gaatje extra in het front waar ik de TTL blokgalf naar buiten laat komen. Klaar.

Nu eens gaan kijken naar die HF signaal generator met de AD9851, dat is voor mij extreem hoog frequent (10MHz) dus een gewone opamp gaat niet meer werken. Ik zit te kijken naar een OPA603, die is nog redelijk in prijs en, heel belangrijk, verkrijgbaar bij mijn lokale hardware boer.

 

Blog op WordPress.com.