JOBlF:Н 'ÝRemote Volume Control door: E v/d Oosterkamp (CCCP). bijlagen: 1 schema. InleidingEnZo Hier een dan een electronica bouwproject voor de gelukkige computer bezitter (iedereen met een Amiga dus..). het vorige bouwproject van mijn hand was meer een theoretisch gewauwel over OpAmps en heb ik zelf nooit gebouwd. Het project dat ik nu ga bespreken heb ik deerentegen wel thuis in gebruik. Nog voor de twijfelaars: Stop hier maar met lezen: dit artikel is serieus... WaarHebbeWeHetOver Een van mijn grote hobby's is het programmeren van mijn trouwe vrindin. Hierbij is een bepaalde dosis muziek onontbeerlijk. Helaas komt (in mijn geval) deze dosis erg nauw. Is het volume te zacht dan erger ik mij groenengeel en kan daardoor niet concentreren. Is het volume te hard dan besteed ik meer aandacht an de muziek dan aan mijn vriendin, die dan ongeduldig met haar cursor knippert. Het tweede probleem is het feit dat mijn stereo aan de andere zijde van mijn 8܌ kamer staat dan mijn computer. Handig zou het pas zijn wanneer ik mijn stereo kon bedienen vanaf mijn computer. Er waren meedere oplossingen: Ik kon een versterker met afstandsbediening kopen. Nu heb ik mijn versterker al een tijdeje, maar hij bevalt mij heel erg en ik heb totaal geen behoefte om hem te vervangen. Ik kon ook zelf een afstands bediening bouwen en met een servo de volume knop draaien. Hiervoor zou ik mijn versterker moeten openen, waar trouwens toch geen plaats voor een servo in is. De uiteindelijke oplossing was: Het volume regelen via mijn computer (RS232) en zonder bewegende delen. Ik wilde een kastje ontwerpen met AudioIn/AudioUit en RS232. Meer zooi niet. HoeLuidtDeOplossing De Firma Crystal levert (onder andere) een chip die het beste kan worden beschreven als een PGA (Programmable Gain Amplifier). Deze chip is stereo uitgevoerd en bedoeld voor het regelen van volume in HighEnd apparatuur. De chip in kwestie is de CS3310 (kost ongeveer twee tientjes). De CS3310 bezit een softwaremute waarbij er totaal geen geluid wordt doorgegeven. Op de volgende pagina staan wat karakteristieke (typical) waarden voor enkele audioparameters en zie waarom dat deze chip in HighEnd toepassingen wordt gebruikt... 8܌ 'Tabelletje met de CS3310 eigenschappen Stapgrootte: 0.5 dB Minimale versterking: 95.5 dB Maximale versterking: 31.5 dB Fout bij 31.5 dB: +0.05 dB Verschil tussen R en L: +0.05 dB Vervorming (2Vrms 1kHz): 0.001 dB Dynamic range: 116 dB Kanaalscheiding: 110 dB Uitgangsruis: 4.2 microVrms Uiteraard wordt het dingetje niet via RS232 aangesproken. Uiteraard zou de paralelle poort van de Amiga ook heel goed voldoen ware het noet dat de kabel te lang zal worden voor een goede communicatie. De makkelijkste oplossing om RS232 om te zetten naar een digitaal signaal dat de CS3310 begrijpt is het gebruiken van een 80C31 microcontroller. Deze bezit namelijk standaard een RS232 poort (mits juist geprogrammeerd) en een aantal parallele I/O poorten. Om het ding met een snelheid van 9600 Baud aan te kunnen spreken gebruik ik een klokkristal met een frequentie van 11.0592MHz. Aangezien ik niet de eerste ben die deze waarde nodig heeft, is dit een standaard kristal en daardoor relatief goedkoop (een gulden of acht tegen anders een aantal tientjes). Dit geldt trouwens ook voor de microcontroller. Display levert het kreng voor het luttele bedrag van f8,95 (!). Hij is dan wel in DIL40 behuizing maar dat maakt verder geen ene moer uit. RS232 maakt gebruik van spanningen rond de 10V (positief en negatief). Deze 8܌ spanningen zijn uiteraard te groot voor de microcontroller. Daarom moet er tussen de controller en de computer nog een RS232 LineDriver worden geplaatst. Hiervan zijn er verschillende in de handel, en de keus komt niet zo nauw. Omdat ik een betrouwbaar en degelijk exemplaar wilde koos ik voor de MAX232 van de firma Maxim. Deze kost ongeveer een tientje. Een van de eigenschappen van de Max232 is dat deze aan een voedingsspanning van 5 Volt voldoende heeft om de +10V/10V RS232 spanningen te creeren. Hiervoor heeft de Max232 intern een schakelende voeding. Terug naar de controller. Om het programma vast te houden gebruik ik uiteraard een EPROM. Het is ook mogelijk om van de 80C31 een versie te verkrijgen die je maar een maal kunt programmeren (OTP Once Time Programmable). Deze controller gaat als 80C51 door het leven en is duurder dan zijn ROMloze broer. Er bestaat ook nog een 87C51. Deze heeft een ingebouwde 4k EPROM inclusief stukje kwartsglas voor het wissen. Deze bestaat alleen in CLCC behuizing en kost rond de honderd piek. Tikje associaal geprijst vergeleken met de 80C31 in DIL14. Naast de EPROM moet er ook nog een adres latch in de schakeling worden opgenomen omdat de databus en de adresbus gemultiplexed zijn. Deze latch kan gewoon uit de HCT serie worden genomen (bijvoorbeeld de 74HCT573) en zal waarschijnlijk maar een paar gulden kosten. 8܌ SoftwareVoorDe80C31 De microcontroller moet uiteraard van software worden voorzien. Hieronder volgt het programma zoals ik dat op het ogenblik gebruik. Zodra de controller van voedingsspanning wordt voorzien initialiseerd deze de RS232 poort voor gebruik met 9600 Baud, 8 databits, 1 stopbit en geen parariteitsbit. Om de CS3310 wakker te schudden rammelt de controller wat met de /Mute van de CS3310. Nadat de controller zichzelf heeft geinitialiseerd stuurt hij via RS232 een string weg met daarin een simpele gebruiksaanwijzing. Met behulp van een terminal(programma) kan deze string worden ontvangen en opdrachten aan de controller worden verstuurd. Na het verzenden van de string wordt de CS3310 zowel links als rechts met de waarde $C0 geinitialiseerd. Dit staat gelijk met een versterking van 0 dB. De software: ******************************************************************************** ; Remote volume control Revisie V1.1 ;Gewenste input: ;Instellen van volume: ; , '1', rechts, links ; 1bh , 31h, C0h , C0h -> beide 0 dB. 8܌ ;----------------------------------------------------------------------------- ; Data declaratie. DSEG AT 30h Vol_Rechts: DS 1 STACKBASE EQU $ ;----------------------------------------------------------------------------- ; Adressen en instellingen. BaudRateDiv EQU 6 ;BaudRate devider voor 9600. NotMute BIT P1.7 ;Active low mute (CS3310). SClock BIT P1.5 ;Serial clock (CS3310). SData BIT P1.4 ;Serial data in (CS3310). CS BIT P1.2 ;Chip enable (CS3310). SIO_Busy BIT 20h.0 ;Serial interrupt bezig/vrij vlag. Heb_Esc BIT 20h.1 ;Onthouden of escape al binnen. Heb_Vol BIT 20h.2 ;Onthouden of juiste code al binnen. Heb_Rechts BIT 20h.3 ;Onthouden of rechter byte al binnen. ;----------------------------------------------------------------------------- ; Verschillende sprong vectoren. CSEG AT 0000h JMP START CSEG AT 0023h PUSH PSW JMP SERVICE_SERIO 8܌ ;============================================================================ ; De main loop. Na initialisatie doet 'ie niks meer. CSEG AT 0100h START: CLR NotMute ;Potmeter geheel dicht. MOV SP,#STACKBASE ;Set de stacks basis. ACALL INIT ;Initializeer wat (RS232). ACALL SEND_STRING ;Zend een string via RS232. SETB CS ;Disable mbv chipselect. SETB NotMute ;Potmeter weer open. CLR CS ;Enable de chip. MOV A,#0C0h ;Het rechtse byte naar de accu. CALL Send_Pot ;Zend het rechtse byte. MOV A,#0C0h ;Het linkse byte naar de accu. CALL Send_Pot ;Zend het linkse byte. SETB CS ;Disable de chip weer. LOOP: JMP LOOP ;Wait till ya weigth an ounce. ;Hier kan nog het een en ander worden verbeterd: De 80C31 kent de mogelijkheid ;van een Idlemode. Hierbij zijn de meeste processor functies uitgeschakeld ;behalve timers, interupts, RAM inhoud en de RS232 poort. Hierdoor word zowel ;het stroomverbruik alswel de hoogfrequente uitstoot teruggedrongen. 8܌ ;============================================================================ ; Initialiseer de hard- en software. INIT: MOV PCON,#10000000b ;SMOD = 1. MOV TMOD,#00100000b ;T1 -> auto-reload. MOV TH1,#256-BaudRateDiv ;BaudRate initializatie. MOV TL1,#256-BaudRateDiv ;Idem. SETB TCON.6 ;Baudrate generator aan. MOV SCON,#01010010b ;MODE = 1. REN=1, TI=1, RI=0. CLR Heb_Rechts ;Geen eerste byte binnen. CLR Heb_Esc ;Nog GEEN escape ontvangen. CLR SIO_Busy ;Serial NIET bezig. MOV IE,#10010000b ;RS232 interrupt aan. RET ;============================================================================ ; Verzend identificatie string via RS232. SEND_STRING: MOV DPTR,#IString ;APTR string naar DPTR. STR_Loop: CLR A ;Wis de accu. MOVC A,@A+DPTR ;Letter -> A(=0) + DPTR naar A. JZ STR_Einde ;Byte nul? Einde string! INC DPTR ;APTR naar volgende letter. ACALL Send_S ;Verzend letter in A. JMP STR_Loop ;Herhaal voor volgende letter. STR_Einde: RET ;Einde string = Einde routine. 8܌ ;----------------------------------------------------------------------------- ; Verzenden van een karakter via RS232. Send_S: JB SIO_Busy,Send_S ;Wacht tot serial klaar is. SETB SIO_Busy ;Serial gaat bezig. MOV SBUF,A ;Serial IS bezig. RET ;============================================================================ ; De serial-interrupt handler voor RS232 SERVICE_SERIO: PUSH ACC ;Bewaar de accumulator. JB RI,SIO_Receive ;Wordt er dan ontvangen? CLR SIO_Busy ;Klaar met zenden.. SIO_END: CLR RI ;Karakter is ontvangen. CLR TI ;Karakter is verzonden. POP ACC ;Haal de accu weer op. POP PSW ;Stond ook nog op stack. RETI ;Einde van de interrupt. ;----------------------------------------------------------------------------- SIO_Receive: MOV A,SBUF ;Lees ontvangen karakter in. JB Heb_Rechts,SIO_Volume ;Alles binnen? Zenden! JB Heb_Vol,SIO_RByte ;Al volume-code binnen? JB Heb_Esc,SIO_Code ;Al escape binnen? CJNE A,#1Bh,SIO_END ;Geen escape? Onbelangrijk! 8܌ SETB Heb_Esc ;Onthoud: Escape al ontvangen. JMP SIO_END ;Einde van de interrupt. SIO_Code: CJNE A,#'1',SIO_STOP ;Geen nieuw volume? Test versie. SETB Heb_Vol ;Onthoud: juiste code binnen. JMP SIO_END ;Einde van de interrupt. SIO_RByte: MOV Vol_Rechts,A ;Bewaar de byte. SETB Heb_Rechts ;Onthoud: Rechterbyte ontvangen. JMP SIO_END ;Einde van de interrupt. ;----------------------------------------------------------------------------- SIO_Volume: PUSH ACC ;Bewaar de laatste byte. CLR CS ;Enable de chip. MOV A,Vol_Rechts ;Het rechtse byte naar de accu. CALL Send_Pot ;Zend het rechtse byte. POP ACC ;haal de linkse byte van stack. CALL Send_Pot ;Zend het linkse byte. SETB CS ;Disable de chip weer. MOV A,#'.' ;Punt naar accu. CALL Send_S ;Send accu via RS232. SIO_STOP: CLR Heb_Esc ;Geen escape meer ontvangen. CLR Heb_Vol ;Geen volume data verwacht. CLR Heb_Rechts ;Geen eerste byte binnen. JMP SIO_END ;Einde interrupt. 8܌;----------------------------------------------------------------------------- Send_Pot: MOV r7,#8 ;Reset teller (aantal bits). CLR SClock ;Klok omlaag. SP_Loop: RLC A ;Rol rechts over de carry. JC SP_One ;Carry gezet? CLR SData ;Nee? Zend een nul(0). SJMP SP_ClockIt ;Verzorg de clock. SP_One: SETB SData ;Ja? Zend een een (1). SP_ClockIt: SETB SClock ;Klok omhoog. CLR SClock ;Klok omlaag. DJNZ r7,SP_Loop ;Loop als er nog bits zijn. RET ;Klaar met zenden. ;============================================================================ IString: DB 0dh,0ah DB 'ETO Remote Volume Control V1.1 (01-06-94)',0dh,0ah DB 0dh,0ah,0dh,0ah DB 'USAGE : 1Bh, 31h, , ',0dh,0ah DB 'VALUES: 01h -> -95.5 dB',0dh,0ah DB ' : 02h -> -95.0 dB',0dh,0ah DB ' : C0h -> 0.0 dB',0dh,0ah DB ' : FFh -> +31.5 dB',0dh,0ah DB 'NOTE: When volume is changed a "." will be returned by the RVC.',0dh,0ah,0dh,0ah,0 ;============================================================================ END 8܌ De 8051 blijkt een instructieset te bezitten die erg lijkt op die van de z80. Vergeleken met de 68000 mnemonics is het oppassen geblazen voor de programmeur: in plaats van "BSET.B #.,.." gebruikt de 8051 "SETB .." maar dat is nog niets vergeleken de MOV instructie. Vergelijken we deze met de MOVE van de 68000 dan wordt het helemaal leuk: Source en Destination zijn verwisseld! Dus in plaats van "MOVE.B #0,Accu" moet je de 8051 vertellen dat hij "MOV A,#0" omgedraaid! Maar hier stop ik met de 8051. Wanneer iemand intresse heeft in een artikel over deze veelzijdige en spotgoedkope controller, dan moet deze me dat maar melden op een ACG clubavond, en anders aan Marc of Matthijs. Ik mis namelijk wel eens een JSWtje, en kan daarom net een message aan mij missen (al denk ik dat ik wel gewaarschuwd word wanneer er een message voor mij instaat). VoedingVanHetHeleSpul Alle onderdelen hebben genoeg aan een 5V voedingsspanning behalve de CS3310. Deze heeft namelijk een symetrische voeding nodig van +5V en 5V. Om een goede (adapter)voeding te verkrijgen wilde ik de totale voedingsspanning zo laag mogelijk houden. Uiteraard is het zonde een onderdeel als de CS3310 met een slechte voeding op te zadelen. Om een stabiele voeding met een lage ingangsspanning te kunnen realiseren heb ik ed relatief dure oplossing gekozen met een schakelende voeding van het type NMH505S. Deze zet een spanning van 5V om in een symmetrisch 5V zoals die voor de CS3310 nodig is. 8܌ Omdat ik van een adapter voedt en deze dingen zelden fatsoenlijk zijn gestabiliseerd, heb ik het ontwerp een knaak duurder gemaakt door de 7805 toe te voegen. Hierdoor kan de ingangsspanning varieren zonder de schakelende voeding op te blazen (+/ 30 gulden). De diode in serie heb ik standaard in al mijn ontwerpen als een soort standaard veiligheids maatregel. Ik ben van mening dat een apparaat tegen foutieve pooling moet kunnen/beveiligd zijn. Rond de CS3310 is een zut aan condensatoren gevestigd. De fabrikant geeft deze onderandere op in een application note. Dat geldt ook voor de weerstand van 10 Ohm. Zowel de Weerstanden als de condensatoren moeten de digitale storingen op de voeding smoren. Deze dienen dan ook zo dicht mogelijk bij de CS3310 gemonteerd te worden. Het is anders met de condensatoren rond de Max232. Deze dienen louter en alleen voor de schakelende voeding in de Max232. Het is uiteraard wenselijk om deze zo dicht mogelijk bij het IC te plaatsen, doch vanwege stoorsignalen wel zo ver mogelijk van de andere onderdelen. Uiteraard moet ieder IC een condensator van 100nF over de voedingslijnen hebben als voorzorgsmaatregel. Deze zijn in het schema niet ingetekend! AmigaSoftware Om het spul vanaf de Amiga aan te kunnen sturen moet er aan die kant natuurlijk ook software worden geschreven. Omdat het flauwekul is een hele source in dit artikel op te nemen geef ik alleen de basisroutines. 8܌ Eerst moeten we serieel informatie versturen. Dit kan met behulp van het serial.device. natuurlijk had ik ook voor het device "SER:" kunnen kiezen, maar ik wil wel wat rappere communicatie. Openen van het serial.device (KS2.0): OpenSer: MOVE.L 4.w,a6 ;a6 = Execbase. JSR CREATEMSGPORT(a6) ;Maak een MsgPort aan. MOVE.L d0,SerMsg ;Bewaar het resultaat. BEQ.B Ser_Fout ;Zekere voor het onzekere. MOVE.L d0,a0 ;Verpoot APTR MsgPort. MOVEQ #82,d0 ;ULONG lengte IOReq-struct. JSR CREATEIOREQUEST(a6) ;Maak IOReq structuur aan. MOVE.L d0,SerReq ;Bewaar de structuur. BEQ.B Ser_Fout ;Zekere voor het onzekere. ;Maak alvast wat voor-instellingen en open het device. MOVE.L SerReq(PC),a1 ;APTR StdIOReq. MOVE.L #9600,io_BAUD(a1) ;ULONG Baudrate. MOVE.B #$80,io_SerFlags(a1) ;UBYTE Vlaggen (Geen protocol) LEA.L SerName(PC),a0 ;APTR naam van het device. MOVEQ #1,d0 ;Unit nummer. MOVEQ #0,d1 ;Geen vlaggen. JSR OPENDEV(a6) ;Uiteindelijk geopend. TST.L d0 ;Test het resultaat. BNE.B Ser_Bezet ;Geen device? Uiteraard kunnen er meer voorinstellingen worden gemaakt. Niet gespecificeerde instellingen worden uit de preferences gehaald. 8܌ Al wat open is moet je ook weer sluiten: SluitSer: MOVE.L 4.w,a6 ;a6 = Execbase. ;Sluit het device en wis de StdIOReq van de console. TST.L SerReq ;Test StdIOReq van serial. BEQ.B Ser_NoReq ;Geen req? Niet wissen! MOVE.L SerReq(PC),a1 ;APTR StdIOReq van console. JSR CLOSEDEV(a6) ;En weg is het device. MOVE.L SerReq(PC),a0 ;APTR StdIOReq van console. JSR DELETEIOREQUEST(a6) ;En weg is de IORequest. CLR.L SerReq ;Wis voor alle zekerheid. ;Wis de messagepoort van de console. Ser_NoReq: TST.L SerMsg ;Test MsgPort van serial. BEQ.B Ser_NoMsg ;Geen MsgPort? Niet wissen! MOVE.L SerMsg(PC),a0 ;APTR MsgPort van console. JSR DELETEMSGPORT(a6) ;En weg is de Messagepoort. Ser_NoMsg: CLR.L SerMsg ;Wis voor alle zekerheid. Zowel de routine voor het openen als de routine voor het sluiten van "serial" zijn alleen geschikt voor Kickstart 2.0 en hoger. Uiteraard moeten dingen als de devicenaam etc nog worden gespecificeerd evenals opslagplaatsen voor de resultaten van de verschillende systeemroutines. 8܌ Na het openen van "serial" (en uiteraard nog voor het sluiten ervan) gebruiken we serial. Dit is niet anders dan het doen van eeen DOIOtje... ChangeVol: MOVE.L 4.w,a6 ;a6 = ExecBase. MOVE.L SerReq(PC),a1 ;APTR SerialIOReq. MOVE.L #4,io_Length(a1) ;ULONG Aantal bytes(4). MOVE.L #SerFile,io_Data(a1) ;APTR Buffer. MOVE.W #CMD_WRITE,io_Command(a1);Schrijf commando. JSR DOIO(a6) ;Stuur commandos naar RVC. SerFile: DC.B $1b,'1',192,192 ;Instellen: 0 dB. Uiteraard is het enige waarin we geinteresseerd zijn het schrijven naar de seriele poort. Volgens het protocol van de Remote Volume Control (zie de 8051 assembly source) moet er eerst en '1' worden ontvangen voordat het volume worst ontvangen. De vier te versturen bytes laten dit nog eens zien. BeschouwendGezwam Het door mij gebouwde prototype trekt een stroom van 100mA. Zoals al eerder werd gemeld kan dit nog worden teruggedrongen door de controller na initialisatie in de idlemode te zetten. In de praktijk is er van Digitale storingen niets terug te vinden in de audiosignalen. De CS3310 belast wanneer hij geen voedingsspanning heeft het ingangssignaal. Het 8܌ is dus niet zondermeer mogelijk met een schakelaar een soort bypass te maken om de CS3310 te omzeilen. Wanneer iemand meer intresse heeft in dit onderwerp/ontwerp of vragen heeft, dan hoeft deze persoon zich alleen maar met mij in verbinding te stellen. Hartelijk dank voor de aandacht... Edwin.