' ######################################################################################## ' A barebones script to support an ' Adafruit MCP2221 breakout board ' using i2c to drive an ' LCD1602 display ' ' A script written for PlodWare Scripter ' 2025-06-07 ' ######################################################################################## newscript ' ----------------------------------- FUNCTION MCP_initialiseDLL () returnValue = ErrorFlag ' do we have a handle to the DLL ' If so we assume that initialisation has already been done. TRY static dim mcpDLL = ErrorFlag END TRY IF (! isError(mcpDLL) ) then ' already initialised EXIT function END if dim mcpDLLfile = "mcp2221_dll_um_x64" mcpDLL = dll.open (mcpDLLfile) IF ( ! mcpDLL ) THEN mcpDLL = ErrorFlag report ( "MCP: Could not open the DLL file: " & mcpDLLfile ) STOP END IF returnValue = mcpDLL ' Dll Driver Links ' void* Mcp2221_OpenByIndex(unsigned int VID, unsigned int PID, unsigned int index) global dim _MCP_OpenByIndex= dll.link(mcpDLL, "Mcp2221_OpenByIndex", 3 ,"unsigned" ) ' int Mcp2221_CloseAll() global dim _MCP_CloseALL= dll.link(mcpDLL, "Mcp2221_CloseAll",0,"signed32" ) ' C functional prototype ' int Mcp2221_I2cWrite(void* handle, unsigned int bytesToWrite, unsigned char slaveAddress, ' unsigned char use7bitAddress, unsigned char* i2cTxData) ' (void*) handle - The handle for the device. ' (unsigned int) bytesToWrite - the number of bytes to write to the slave. Valid range is ' between 0 and 65535. ' (unsigned char) slaveAddress - 7bit or 8bit I2C slave address, depending on the value of the ' "use7bitAddress" flag.For 8 bit addresses, the R/W LSB of the ' address is set to 0 inside the function. ' (unsigned char) use7bitAddress - if >0 7 bit address will be used for the slave. If 0, 8 bit is used. ' (unsigned char*) i2cTxData - buffer that will contain the data bytes to be sent to the slave. ' Returns: (int) - 0 for success; error code otherwise. ' NOTE: if the "Mcp2221_SetSpeed" function has not been called for the provided handle, the default ' speed of 100kbps will be configured and used. Otherwise, the global dim _MCP_I2CWrite = dll.link(mcpDLL, "Mcp2221_I2cWrite",5,"signed32" ) ' define global variables global dim MCP_defaultVID = 0x4D8 global dim MCP_defaultPID = 0x0DD global dim mcp_Handle = 0 END Function ' ----------------------------------- ' If/when you gain access to a specific mcp2221 device, the device driver registers the fact ' and does not allow someone else to access that device. ' pwScripter is intelligent enough to release access to the DLL when pwScripter terminates ' or when it executes a newScript command. ' BUT pwScripter can not know that it should do more. UNLESS you tell it! ' This function is how you tell pwScripter what specially handling is required ' when pwScripter terminates or executes a newScript command. ' FUNCTION OnNewScript () TRY _MCP_CloseALL () END TRY inherit OnNewScript () ' calls any PRIOR onNewScript function i.e. they can be daisy-chained ' after execution THIS onNewScript will be unchained and deleted. END FUNCTION ' ----------------------------------- FUNCTION mcp_open_device_by_id ( optional deviceId = 0 ) ' returns a MCP device handle ' or ErrorFlag on error returnValue = _MCP_OpenByIndex ( MCP_defaultVID , MCP_defaultPID , deviceId ) IF ( returnValue == -1 ) then ' INVALID_HANDLE_VALUE= -1 according to the DLL user guide Report("mcp2221a DRIVER is accessible, but device index: " & deviceId & " not responding" ) Report("BE WARNED: if you DO NOT plug the USB-C firmly in to the mcp2221 the red led might light up" ) Report("but the device is not accessible. (At least on my hardware!)" ) returnValue = errorFlag END IF END function ' ----------------------------------- ' ######################################################################################## ' now start the non-generic code ' This code is for a LCD1602 driven in 4-bit bit-bang mode via ' an Heevhas I2C IIC Serial Interface Adapter Plate ' ######################################################################################## dim lcd1602_i2cAddr = 0x27 dim i2cAddr = lcd1602_i2cAddr ' ----------------------------------- ' wrappers around the MCP2221A I2C functions so that the LCD1602 I2C code is "generic" ' ----------------------------------- FUNCTION i2cWrite ( optional handle = mcp_Handle, nbrBytesToWR , optional slvAdd = i2cAddr, optional AddrSize = 7, dataArray ) returnValue = _MCP_i2cWrite ( handle , nbrBytesToWR , slvAdd, AddrSize , dataArray ) END function FUNCTION i2cRead ( optional handle = mcp_Handle, nbrBytesToRD , optional slvAdd = i2cAddr, optional AddrSize = 7 ) returnValue = _MCP_i2cRead ( handle , nbrBytesToRD , slvAdd , AddrSize ) END function ' ----------------------------------- ' these functions ARE SPECIFIC ' to using I2C to talk to the LCD1602 ' ----------------------------------- FUNCTION sendByte ( val ) dim dataArray = byteArray ( 2 , val BOR 0x08 ) ' keep back-lighting on i2cWrite ( , 1 ,lcd1602_i2cAddr ,7, dataArray ) END Function FUNCTION lcdCmd ( val ) ' send upper 4 bits dim outByte = ( val BAND 0xf0 ) BOR 0x04 sendByte ( outByte ) outByte = outByte BAND 0xfb sendByte ( outByte ) outByte = ( ( val BAND 0x0f ) lshift 4 ) BOR 0x04 sendByte ( outByte ) outByte = outByte BAND 0xfb sendByte ( outByte ) END function FUNCTION lcdChar ( val ) ' send upper 4 bits dim outByte = ( val BAND 0xf0 ) BOR 0x05 sendByte ( outByte ) outByte = outByte BAND 0xfb sendByte ( outByte ) outByte = ( ( val BAND 0x0f ) lshift 4 ) BOR 0x05 sendByte ( outByte ) outByte = outByte BAND 0xfb sendByte ( outByte ) END function FUNCTION lcdPrintFast ( uText ) dim outArray = bytearray ( 128 ) dim outIndex = 0 dim i,val,tmp FOR i = 0 to length(uText) -1 val = asc(mid(utext,i,1)) tmp = ( val BAND 0xf0 ) BOR 0x0d outArray(outIndex++) = tmp outArray(outIndex++) = tmp BAND 0x0fb tmp = ( ( val BAND 0x0f ) lshift 4 ) BOR 0x0d outArray(outIndex++) = tmp outArray(outIndex++) = tmp BAND 0x0fb NEXT i i2cWrite ( , outIndex ,lcd1602_i2cAddr ,7, outArray ) END function FUNCTION defineCustomCharFast ( id, r0,r1,r2,r3,r4,r5,r6,r7) dim outArray = bytearray ( 128 ) dim outIndex = 0 dim i,val,tmp,row val = 0x40 BOR (id*8) tmp = ( val BAND 0xf0 ) BOR 0x04 outArray(outIndex++) = tmp outArray(outIndex++) = tmp BAND 0x0fb tmp = ( ( val BAND 0x0f ) lshift 4 ) BOR 0x04 outArray(outIndex++) = tmp outArray(outIndex++) = tmp BAND 0x0fb FOR row = 0 to 7 val = arg(row + 1) BAND 0x0ff tmp = ( val BAND 0xf0 ) BOR 0x0d outArray(outIndex++) = tmp outArray(outIndex++) = tmp BAND 0x0fb tmp = ( ( val BAND 0x0f ) lshift 4 ) BOR 0x0d outArray(outIndex++) = tmp outArray(outIndex++) = tmp BAND 0x0fb NEXT row i2cWrite ( , outIndex ,lcd1602_i2cAddr ,7, outArray ) END function ' ----------------------------------- ' these functions are INDEPENDENT of whether we ' are using bit-bang or I2C to talk to the LCD1602 ' ----------------------------------- FUNCTION defineCustomChar ( id, r0,r1,r2,r3,r4,r5,r6,r7) dim row lcdCmd ( 0x40 BOR (id*8) ) ' set custom character addressing. ID is 0..7 FOR row = 0 to 7 lcdChar ( arg(row + 1) ) NEXT row END function FUNCTION lcdPrint ( uText ) dim i,ch FOR i = 0 to length(uText) -1 lcdChar ( asc(mid(utext,i,1)) ) NEXT i END function FUNCTION lcdRC ( row , col ) col= cint(col) BAND 0x3f IF row then lcdCmd ( 0xc0 BOR col) ELSE lcdCmd ( 0x80 BOR col) END if END function FUNCTION lcdInit ( ) lcdCmd(0x33) ' set in 8-bit mode lcdCmd(0x32) ' change to 4-bit mode lcdCmd(0x28) ' Function set : 4-bit, 2-lines, 5x8 font) lcdCmd(0x0d) ' 0x08 = cmd controls the display: 0x04 = Turn the display on, 0x02= cursor ON, 0x01 = cursor to blinking END function ' ######################################################################################## ' active LCD code starts here ' ######################################################################################## ' prepare the MCP2221A for the demo MCP_initialiseDLL () mcp_Handle = mcp_open_device_by_id (0) ' ----------------------------------- ' initialise the LCD lcdInit () ' ----------------------------------- ' create some custom characters, for demonstration purposes lcdCmd ( 0x01)' Clear the display defineCustomCharFast ( 0,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa) defineCustomCharFast ( 1,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55) ' ----------------------------------- ' now display something lcdCmd ( 0x01)' Clear the display lcdPrint("slow") lcdRC (1,2) ' first row, second column lcdPrint ( chr(0) & "Hello World!" & chr(1)) sleep(5000) ' wait 5 seconds lcdCmd ( 0x01)' Clear the display lcdPrintFast("fast") lcdRC (1,2) ' first row, second column lcdPrintFast ( chr(0) & "Hello World!" & chr(1))