' AFF2AE090B88AFA7C7784E7FF5F7CC69ED97FB64F837B2045FF20ED2B0A22B6DE98DB770A7013F0F22EE3C3DC4E28C3A767449ECD27192FC346A0EBD605015ED ' sRipeTech ' 45A7F6DB34 2024-08-05 17:50:04 ' mini demo for driving a ' SPARKFUN Qwiic Button Shield ' via an Adafruit MCP2221A Breakout - General Purpose USB to GPIO ADC I2C - Stemma QT / Qwiic board ' found the dll "mcp2221_dll_um_x64" at ' https://www.microchip.com/en-us/product/MCP2221A be careful get the latest version, I have v2.2.1 ' it is in a ZIP file along with the MCP2221 Dll User Guide.pdf. There are two versions of the DLL in the zip ' one for managed code, on for unmanaged. ' I 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! ' you will also need the Qwiic Button I2C Register Map which I found at ' https://cdn.sparkfun.com/assets/learn_tutorials/1/1/0/8/Qwiic_Button_I2C_Register_Map.pdf ' ----------------------------------- ' the following is general housekeeping when starting a new script: 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 "sort-of" interchangeable EXCEPT that the SmBus is limited to 100 kb/s, I2C can go faster dim I2CRead = dll.link(myDLL, "Mcp2221_I2cRead",5,"signed32" ) ' returns 0 = success dim I2CWrite = dll.link(myDLL, "Mcp2221_I2cWrite",5,"signed32" ) ' returns 0 = success dim SmbusReadByte = dll.link(myDLL, "Mcp2221_SmbusReadByte",6,"signed32" ) ' returns 0 = success dim SmbusWriteByte = dll.link(myDLL, "Mcp2221_SmbusWriteByte",6,"signed32" ) ' returns 0 = success ' ----------------------------------- ' get a handle for a specific MCP2221 device dim handle handle = 0 handle = OpenByIndex ( defaultVID , defaultPID , 0 ) ' have chosen the device index = 0 IF ( handle == -1 ) THEN report("Could not connect to device index 0" ) STOP END IF report ( "DLL handle = " & hex(myDll) ) report ( "mcp device handle = " & hex ( handle) ) STOP ' at this point the MCP2221A is ready for I2C and smBus communications ' ######################################################################################## ' From here on we are concerned with the ' SPARKFUN Qwiic Button Shield ' connect QwiicButton using the Stemma QT connector ' Technically a 4 pin JST PH connector. ' NO special configuration needed ' ######################################################################################## dim res ' a temporary value CONSTANT QwiicButtonAddress = 0x6f ' I have implemented 2 ways to read from the SPARKFUN Qwiic Button Shield ' 1 ) using smBus commands (nice and simple) ' 2 ) using i2c, a little bit more complicated because you must first tell (i2cWRdata) ' the device which register you want to read in the next I2cRead operation ' Techie note: when a DLL interface needs the address of a string or an array, then ' it is common in some programming languages ( e.g. 'C') to place a @ before the value. ' You can do this is pwScripter. ' OR ' pwScripter checks the type of a parameter being passed to a DLL function, and if it does not evaluate ' to a single value, then pwScripter automatically passes the address of the parameter. So ' you do not have to provide the @ yourself. But providing it is harmless! ' BUT ' If the DLL interface needs the address of a single value then you MUST provide the @ yourself. ' OR OR OR: see the ReadQwiicButton function below. ' Say a DLL parameter is supposed to be a ptr to a 64 bit value ' 1) dim fred = 1234 ' and pass @ fred ' OR ' 2) dim fred = qwordArray (1) ' fred (0) = 1234 ' and simply fred FUNCTION WriteQwiicButton ( regAdd , WriteByte) ' note: the last-but-one argument "Command Code" = the target Register address ' for the SPARKFUN Qwiic Button shield SmbusWRITEByte(handle, QwiicButtonAddress , 1 , 0 , regAdd , WriteByte ) END Function FUNCTION ReadQwiicButton ( regAdd ) dim SingleByte = 0 ' note: the last-but-one argument "Command Code" = the target Register address ' for the SPARKFUN Qwiic Button shield ' note: we have to use a @ since SingleByte is a single variable. ' IF we had defined SingleByte as bytearray (1) we would NOT have needed the @ returnValue = SmbusReadByte(handle, QwiicButtonAddress , 1 , 0 , regAdd , @ SingleByte ) IF ( returnValue ) then returnValue = errorFlag ELSE returnValue = SingleByte END if END Function FUNCTION i2cReadQwiicButton ( I2Caddress , regAdd ) ' Note that to read from a SPARKFUN Qwiic Button Shield using the I2C interfaces ' of the MPC2221A chip, you must first WRITE to the shield telling it which ' register you want to read from! ' here is the write byte string, actually 1 byte is needed, but habit makes me over dimension arrays dim i2cWRdata = byteArray (2, regAdd, 0) ' bytearray of 1 element, initialised to regadd, 0 i2cWrite(handle, 1, I2Caddress, 1, i2cWRdata) dim readBuffer = bytearray (1) ' will be written to by the shield IF ( I2cRead( handle , 1 , I2Caddress , 1 , readBuffer ) ) then ' have an error returnValue = errorFlag ELSE returnValue = readBuffer ( 0 ) END IF END FUNCTION STOP ' have created helper functions to read/write the Qwiic button shield ' ###################################################################### ' verify access to the SPARKFUN Qwiic Button Shield ' the default contents of Reg 0x00 = 0x5d res = i2cReadQwiicButton(QwiicButtonAddress, 0x00 ) ' read using i2c interface IF ( isError(res) ) then report ( "ERROR: I2C read of reg 0x00 failed") ELSE report ( "button id = reg 0x00 = " & hex( res ) ) END if res = ReadQwiicButton(0x1f)' read using SmBus interface IF ( isError(res) ) then report ( "ERROR: SmBus read of reg 0x1f failed") ELSE report ( "button i2c address = reg 0x1f = " & hex( res ) ) END if STOP ' we have access to the Qwiic button shield ' ###################################################################### ' set up some LED pulsing/flashing WriteQwiicButton ( 0x019 , 0x0f) ' brightness WriteQwiicButton ( 0x01a , 0x08) ' steps to brightness WriteQwiicButton ( 0x01b , 0xf4) ' low byte how long in ms it takes to cycle from OFF-programmed brightness-OFF WriteQwiicButton ( 0x01c , 0x0f) ' high byte WriteQwiicButton ( 0x01d , 0xf4) ' low byte how long in ms between two cycles of OFF-programmed brightness-OFF WriteQwiicButton ( 0x01e , 0x0f) ' high byte STOP ' ###################################################################### ' stop LED pulsing/flashing WriteQwiicButton ( 0x019 , 0x00) ' min brightness WriteQwiicButton ( 0x01b , 0x00) ' low byte how long in ms it takes to cycle from OFF-programmed brightness-OFF WriteQwiicButton ( 0x01c , 0x00) ' high byte WriteQwiicButton ( 0x01d , 0x00) ' low byte how long in ms between two cycles of OFF-programmed brightness-OFF WriteQwiicButton ( 0x01e , 0x00) ' high byte STOP ' ###################################################################### ' LED full on WriteQwiicButton ( 0x019 , 0xff ) ' max brightness STOP ' ###################################################################### ' LED full off WriteQwiicButton ( 0x019 , 0x00 ) ' min brightness STOP ' ###################################################################### ' read button status using smBus res = ReadQwiicButton ( 0x03 ) BAND 0x04 IF ( res ) then report("Button pressed") ELSE report("Button NOT pressed") END IF STOP ' ###################################################################### ' read button status using i2c res = i2cReadQwiicButton(QwiicButtonAddress, 0x03 ) BAND 0x04 IF ( res ) then report("Button pressed") ELSE report("Button NOT pressed") END IF STOP ' ###################################################################### ' more adventurous demo ' stop the built in LED pulsing routines WriteQwiicButton ( 0x01b , 0x00) ' low byte how long in ms it takes to cycle from OFF-programmed brightness-OFF WriteQwiicButton ( 0x01c , 0x00) ' high byte WriteQwiicButton ( 0x01d , 0x00) ' low byte how long in ms between two cycles of OFF-programmed brightness-OFF WriteQwiicButton ( 0x01e , 0x00) ' high byte FUNCTION stopTimerA () timer ( 0, 0 , 0 , "cbTimerA" ) END FUNCTION FUNCTION cbTimerA ( timerId ) static dim oldState = 0 ' we want these values to be available each time this function is called static dim ledState = 0 ' with the potentially updated values static dim cntState = 0 ' could have used global variables, but that is poor programming practice dim newState = ReadQwiicButton ( 0x03 ) BAND 0x04 ' only want the button on/off status bit IF ( newState == oldState ) then ' no change , if state is pressed then increment the "going to stop count" IF ( newState ) then cntState++ ' note use of single line IF .. THEN .. statement ELSEIF ( newState == 0 ) then ' we only toggle the LED on off->on changes ELSE ' we have a button off->on change cntState = 0 ledState = ! ledState ' toggle the LED IF ( ledState ) then WriteQwiicButton ( 0x019 , 0xff ) ' max brightness ELSE WriteQwiicButton ( 0x019 , 0x00 ) ' min brightness END IF END IF oldState = newState IF ( cntState > 11 ) then cntState = 0 ' in case you restart the timer manually later on WriteQwiicButton ( 0x019 , 0x00 ) ' min brightness ELSE timer ( 0, 0 , 250 , "cbTimerA" ) ' retrigger the timer END IF END function timer ( 0, 0 , 250 , "cbTimerA" ) STOP