' BA164833CFEF1E253BFE9DD1BA6F87C42C3B9A1A567D6D97991CCB7C34A6A244DCB657F59936A7A2097187793C57FA301B5CA038F9965F3F1250A8C5C0125336 ' sRipeTech ' 4CB220C9FC 2024-07-17 18:22:50 ' C:\Users\george\Desktop\pwScripter\ns-mcp2221a-tea5767\ns-mcp2221a-tea5767.txt ' stripped down demo for driving a ' ICQUANZX TEA5767 FM stereo radio ' via an Adafruit MCP2221A Breakout - General Purpose USB to GPIO ADC I2C - Stemma QT / Qwiic board ' ----------------------------------------------------------------------------------- ' found the dll "mcp2221_dll_um_x64", inside a ZIP file, at ' https://www.microchip.com/en-us/product/MCP2221A ' Also in the ZIP file was the MCP2221 DLL User Guide. ' Copied the unmanaged 64 bit dll to C:\Windows\System32\ ' pwS is written in "plain-old C" it is an "unmanaged" application, so be sure that the DLL in the "_um_" one! ' ----------------------------------------------------------------------------------- ' I could not find a web-site for ICQUANZX, but I found the TEA5767 product data sheet at ' https://www.sparkfun.com/datasheets/Wireless/General/TEA5767.pdf ' other online searches suggest that the core chip is from Philips Semiconductors newscript ' effectively empties pwS of any previous script report ( ) ' clears the report panel report ' causes the report panel to be visible ' ######################################################################################## ' pwS functions to access the functions exposed by the DLL "mcp2221_dll_um_x64" ' Have only linked to the bare minimum of the DLL's functions needed for this demo ' ######################################################################################## dim myDLL ' global variable that identifies the opened DLL myDLL = dll.open ("mcp2221_dll_um_x64") IF ( myDLL == 0 ) THEN Message ="Could not open the DLL file" STOP END IF ' Microchip default dim defaultVID = 0x4D8 dim defaultPID = 0x0DD ' ----------------------------------- dim OpenByIndex = dll.link(myDLL, "Mcp2221_OpenByIndex", 3 ,"unsigned" ) dim CloseALL = dll.link(myDLL, "Mcp2221_CloseAll",0,"signed32" ) FUNCTION OnNewScript () CloseALL () inherit OnNewScript () END FUNCTION ' ----------------------------------- ' SmBus and I2C are interchangeable EXCEPT that the SmBus is limited to 100 kb/s, I2C can go faster dim I2CRead = dll.link(myDLL, "Mcp2221_I2cRead",5,"signed32" ) dim I2CWrite = dll.link(myDLL, "Mcp2221_I2cWrite",5,"signed32" ) ' ######################################################################################## ' it seems that IF you do not specifically set the I2C baud rate ' that it defaults to 100k ' To set the baudrate ' dim SetSpeed = dll.link(myDLL, "Mcp2221_SetSpeed",2,"signed32" ) ' SetSpeed(handle, 500000) ' between 46875 and 500000 ' ######################################################################################## ' ----------------------------------- ' get a handle for a specific MCP2221 device dim handle handle = 0 handle = OpenByIndex ( defaultVID , defaultPID , 0 ) ' have chosen the device index = 0 report = " Handle: " & hex(handle) STOP ' MCP2221A now set-up from I2C communications ' next we set the TEA5767 to a local radio ( 93.1 Mhz in my current region) ' ######################################################################################## ' TEA5766 FM Stereo radio ' connect TEA5767 using the standard I2C pins ' NO special configuration needed ' ######################################################################################## ' USE 3.3 V not 5.0 V !! ' Why, do not know, but on my device it only worked with 3.3 V ' ######################################################################################## ' TEA5767 I2C address = 0x60 dim I2Caddress = 0x60 ' first byte constants dim MuteOn = 0x80 dim MuteOff = 0x00 dim SearchOn = 0x40 dim SearchOff = 0x00 ' third byte constants dim SearchUp = 0x80 dim SearchDn = 0x00 dim SearchStopLevelHigh = 0x60 dim SearchStopLevelMed = 0x40 dim SearchStopLevelLow = 0x20 dim HighSideInj = 0x10 dim LowSideInj = 0x00 ' forth byte constants dim blJapan = 0x20 ' Band Limits dim blEurope = 0x00 dim HCCOn = 0x04 ' High Cut Control dim HCCOff = 0x00 dim SNCOn = 0x02 ' Stereo Noice Cancelling dim SNCOff = 0x00 dim searchDelta = 6 ' about 50kHz dim StepDelta = 6 ' about 50kHz ' ######################################################################################## ' The most direct use of the TEA5767 is to simply set a frequency ' ----------------------------- ' helper function to force a frequency to be on a 25kHz raster FUNCTION rastorise (freq) ' rastorise frequency (on 25 kHz boundaries) dim freqI = cint ( freq ) ' integer part of frequency (in Mhz) dim freqf = freq - freqI ' fractional part of frequency (in Mhz) freqf = ( (( freqf * 1000 ) + 24) / 25 ) rastorise = freqi + (cint (freqf) / 40 ) END FUNCTION FUNCTION setFreq ( frequency ) frequency = rastorise ( frequency ) report() report report(frequency) ' frequency in MhZ dim frequencyB = ( 4*(( frequency*1000000 ) +225000))/32768 ' calculating PLL word dim frequencyH = ( cint (frequencyB) rshift 8 ) band 0x0ff dim frequencyL = cint (frequencyB) band 0x0ff ' There are 2 ways to initialise an array ! ' the short way: ' dim i2cWRdata = byteArray (6 , frequencyH , frequencyL , HighSideInj , 0x10 , 0x00 ) ' or the long way: dim i2cWRdata = byteArray (6) i2cWRdata (0) = frequencyH i2cWRdata (1) = frequencyL i2cWRdata (2) = HighSideInj i2cWRdata (3) = 0x10 ' = blEurope and XTAL = 1 i2cWRdata (4) = 0x00 dim res = i2cWrite(handle, 5, I2Caddress, 1, i2cWRdata) END function setFreq ( 93.1 ) ' is WDR3 in some parts of Germany region STOP ' ######################################################################################## ' next we will nudge the current frequencey up / down by a fixed step dim stepFreq = 0.025 ' = 25 kHz FUNCTION getFreq ( ) ' get the current PLL value ' i.e. what the radio is tuned to dim readBuffer = bytearray (6) ' will be written to by the shield IF ( I2cRead( handle , 5 , I2Caddress , 1 , readBuffer ) ) then ' have an error returnValue = errorFlag EXIT function END IF dim PLL = ( readBuffer(0) BAND 0x03f ) * 0x100 PLL = PLL + readBuffer(1) ' convert the PLL value to frequency pll = (readBuffer(0) BAND 0x03f ) * 0x0100 pll = pll + readBuffer(1) dim freq = ( ( ( pll * 32768 ) / 4 ) - 225000 )/1000000 ' rastorise frequency (on 25 kHz boundaries) dim freqI = cint ( freq ) dim freqf = freq - freqI freqf= ( (( freqf * 1000 ) + 24) / 25 ) getFreq = freqi + (cint (freqf) / 40 ) END FUNCTION FUNCTION dn(optional rate = 1) setFreq ( getFreq () - (rate * stepFreq )) END function FUNCTION Up(optional rate = 1) setFreq ( getFreq () + (rate * stepFreq )) END function STOP ' ######################################################################################## ' WARNING: ' To be honest I am REALLY not happy with scanning ' The SCANUP function works but does not always find the expected channels ' SCANDN is even worse. ' It almost seems as if the scan results is dependent on the initial frequency ' of the receiver when the scan is triggered. FUNCTION scanWait () ' wait for the scan process to finish dim readBuffer = bytearray (6) ' will be written to by the shield IF ( I2cRead( handle , 5 , I2Caddress , 1 , readBuffer ) ) then ' have an error returnValue = errorFlag EXIT function END IF ' strictly a loopCnt should not be needed. ' BUT: it ensures that the loop is not infinite, which might happen ' if someone pulls out a cable or ... dim loopCnt = 10 DO while ( ( readBuffer(0) BAND 0x80 == 0 ) AND ( loopCnt -- ) ) sleep ( 200 ) IF ( I2cRead( handle , 5 , I2Caddress , 1 , readBuffer ) ) then ' have an error returnValue = errorFlag EXIT function END IF LOOP ' turn OFF the MUTE dim i2cWRdata = byteArray (6) i2cWRdata (0) = readBuffer(0) BAND 0x3f i2cWRdata (1) = readBuffer(1) i2cWRdata (2) = HighSideInj i2cWRdata (3) = 0x10 ' = blEurope and XTAL = 1 i2cWRdata (4) = 0x00 dim res = i2cWrite(handle, 5, I2Caddress, 1, i2cWRdata) ' then read the new frequency and display it report() report report ( getFreq ( ) ) END FUNCTION FUNCTION scanDn(optional rate = 1) ' ---------------------------------- ' want to get the current frequency ' and shift it DOWN dim frequency = rastorise ( getFreq( ) - (rate * 4 * stepFreq ) ) report( frequency ) IF ( frequency < 88.0 ) then frequency = 108.0 END IF dim frequencyB = ( 4*(( frequency*1000000 ) +225000))/32768 ' calculating PLL word dim frequencyH = ( cint (frequencyB) rshift 8 ) band 0x0ff dim frequencyL = cint (frequencyB) band 0x0ff ' ---------------------------------- ' now set the start frequency of the search, and start the search dim i2cWRdata = byteArray (6) i2cWRdata (0) = frequencyH BOR SearchON BOR MuteOn i2cWRdata (1) = frequencyL i2cWRdata (2) = Searchdn BOR SearchStopLevelHigh BOR HighSideInj i2cWRdata (3) = 0x10 ' = blEurope and XTAL = 1 i2cWRdata (4) = 0x00 dim res = i2cWrite(handle, 5, I2Caddress, 1, i2cWRdata) scanWait () END function FUNCTION scanUp(optional rate = 1) ' ---------------------------------- ' want to get the current frequency ' and shift it UP dim frequency = rastorise ( getFreq( ) + (rate * 4 * stepFreq ) ) IF ( frequency > 108.0 ) then frequency = 88.0 END IF dim frequencyB = ( 4*(( frequency*1000000 ) +225000))/32768 ' calculating PLL word dim frequencyH = ( cint (frequencyB) rshift 8 ) band 0x0ff dim frequencyL = cint (frequencyB) band 0x0ff ' ---------------------------------- ' now set the start frequency of the search, and start the search dim i2cWRdata = byteArray (6) i2cWRdata (0) = frequencyH BOR SearchON BOR MuteOn i2cWRdata (1) = frequencyL i2cWRdata (2) = SearchUp BOR SearchStopLevelHigh BOR HighSideInj i2cWRdata (3) = 0x10 ' = blEurope and XTAL = 1 i2cWRdata (4) = 0x00 dim res = i2cWrite(handle, 5, I2Caddress, 1, i2cWRdata) scanWait () END function scanUp() STOP ' ######################################################################################## ' now control the frequency of the receiver via an Analogue Digital Convertor ' ----------------------------- ' will use the G1 pin of the mcp2221a board ' and set it to ADC mode dim SetGpioSettings = dll.link(myDLL, "Mcp2221_SetGpioSettings",5,"signed32" ) FUNCTION SetMPCGPIOsettings ( handle ) ' this is provided as a template for you to define your own Function/Direction/Values ' e.g. initialise these 3 arrays to have your default values dim pinFunction = bytearray ( 4 , 0 , 2 , 2 , 2 ) ' 0 = GPIO, 2=ADC dim pinDirection = bytearray ( 4 , 0 , 0 , 0 , 0 ) ' 0 = OUT dim pinValues = bytearray ( 4 , 1 , 1 , 1 , 1 ) ' 0 = LOW 1 = HIGH setGpioSettings( handle , 1, pinFunction ,pinDirection , pinValues ) END function SetMPCGPIOsettings ( handle ) ' ----------------------------- ' although we are using the G1 pin, when accessing it's ADC value ' it is the first ADC element ' i.e. in the function getADCn G1 ADC value is obtained with n=0 dim GetAdcData = dll.link(myDLL, "Mcp2221_GetAdcData",2,"signed32" ) FUNCTION getADCn ( handle , n) dim ADCvalues = dwordArray ( 4 ) returnValue = GetAdcData ( handle , ADCvalues ) IF (returnValue ) then returnValue = errorFlag ELSE returnValue = ADCValues(n) END if END function ' ----------------------------- ' this is a timer routine that regularly reads the ADC, and if it has changed then update the ' receiver's frequency ' WARNING: ' sadly I found that even when the potentiometer that created the ADC analogue signal ' was not being changed, the ADC digital vaue was NOT constant. ' It appeared to be swinging by about +- 2, sometimes by +- 4 ' ' Minor success: The major swings were when I connected the MCP2221A board ' directly to my laptop (running on mains power). ' When I connected it to the USB-C power-hub that was charging the laptop the ' swing settled down to +- 1 ' Weird. Running off the laptop battery only, was also +- 1 FUNCTION cbTimera ( timerId ) static dim oldADC = 0 ' we want these values to be available each time this function is called dim newADC = getADCn ( handle , 1) ' So it was necessary to apply a filter to the ADC value! IF ( ( newADC > oldADC + 2) OR ( newADC < oldADC - 2) ) then setFreq ( rastorise( ((108.0 - 88.0) * newADC / 1024 ) + 88.0 ) ) oldADC = newADC END IF timer ( 0, 0 , 100 , "cbTimerA" ) ' retrigger the timer END function FUNCTION stopTimerA () timer ( 0, 0 , 0 , "cbTimerA" ) END FUNCTION timer ( 0, 0 , 250 , "cbTimera" ) ' ----------------------------- ' Thats all