/*********************************************************************************************************************** PicoMite MMBasic I2C.c Geoff Graham, Peter Mather Copyright (c) 2021, All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed on the console at startup (additional copyright messages may be added). 4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the . 5. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ************************************************************************************************************************/ /** * @file I2C.c * @author Geoff Graham, Peter Mather * @brief Source for I2C MMBasic commands */ /** * @cond * The following section will be excluded from the documentation. */ #include "MMBasic_Includes.h" #include "Hardware_Includes.h" #include "hardware/i2c.h" #include "hardware/irq.h" #define nunaddr 0xA4 / 2; #define PinRead(a) gpio_get(PinDef[a].GPno) extern void DrawBufferMEM(int x1, int y1, int x2, int y2, unsigned char * p); extern void ReadBufferMEM(int x1, int y1, int x2, int y2, unsigned char * buff); // Declare functions void i2cEnable(unsigned char * p); void i2cDisable(unsigned char * p); void i2cSend(unsigned char * p); void i2cSendSlave(unsigned char * p, int channel); void i2cReceive(unsigned char * p); void i2c_disable(void); void i2c_enable(int bps); void i2c_masterCommand(int timer, unsigned char * buff); void i2cCheck(unsigned char * p); void i2c2Enable(unsigned char * p); void i2c2Disable(unsigned char * p); void i2c2Send(unsigned char * p); void i2c2Receive(unsigned char * p); void i2c2_disable(void); void i2c2_enable(int bps); void i2c2_masterCommand(int timer, unsigned char * buff); void i2c2Check(unsigned char * p); static MMFLOAT * I2C_Rcvbuf_Float; // pointer to the master receive buffer for a MMFLOAT static long long int * I2C_Rcvbuf_Int; // pointer to the master receive buffer for an integer static char * I2C_Rcvbuf_String; // pointer to the master receive buffer for a string static unsigned int I2C_Addr; // I2C device address static volatile unsigned int I2C_Sendlen; // length of the master send buffer static volatile unsigned int I2C_Rcvlen; // length of the master receive buffer static unsigned char I2C_Send_Buffer[256]; // I2C send buffer bool I2C_enabled = false; // I2C enable marker unsigned int I2C_Timeout; // master timeout value volatile unsigned int I2C_Status; // status flags int mmI2Cvalue; // value of MM.I2C static MMFLOAT * I2C2_Rcvbuf_Float; // pointer to the master receive buffer for a MMFLOAT static long long int * I2C2_Rcvbuf_Int; // pointer to the master receive buffer for an integer static char * I2C2_Rcvbuf_String; // pointer to the master receive buffer for a string static unsigned int I2C2_Addr; // I2C device address static volatile unsigned int I2C2_Sendlen; // length of the master send buffer static volatile unsigned int I2C2_Rcvlen; // length of the master receive buffer //static unsigned char I2C_Send_Buffer[256]; // I2C send buffer bool I2C2_enabled = false; // I2C enable marker unsigned int I2C2_Timeout; // master timeout value volatile unsigned int I2C2_Status; // status flags //static char I2C_Rcv_Buffer[256]; // I2C receive buffer static unsigned int I2C_Slave_Addr; // slave address char * I2C_Slave_Send_IntLine; // pointer to the slave send interrupt line number char * I2C_Slave_Receive_IntLine; // pointer to the slave receive interrupt line number //static char I2C2_Rcv_Buffer[256]; // I2C receive buffer char * I2C2_Slave_Send_IntLine; // pointer to the slave send interrupt line number char * I2C2_Slave_Receive_IntLine; // pointer to the slave receive interrupt line number static unsigned int I2C2_Slave_Addr; // slave address bool noRTC = false, noI2C = false; extern void SaveToBuffer(void); extern void CompareToBuffer(void); extern void DrawPixelMEM(int x1, int y1, int c); extern void DrawRectangleMEM(int x1, int y1, int x2, int y2, int c); extern void DrawBitmapMEM(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char * bitmap); void i2cSlave(unsigned char * p); void i2c2Slave(unsigned char * p); void i2cReceiveSlave(unsigned char * p, int channel); int CameraSlice = -1; int CameraChannel = -1; extern void PWMoff(int slice); const unsigned char nuninit[2] = { 0xF0, 0x55 }; const unsigned char nuninit2[2] = { 0xFB, 0x0 }; const unsigned char readcontroller[1] = { 0 }; const unsigned char nunid[1] = { 0xFC }; const unsigned char nuncalib[1] = { 0x20 }; volatile uint8_t classic1 = false, nunchuck1 = false; uint8_t nunbuff[10]; uint32_t swap32(uint32_t in ) { in = __builtin_bswap32(in); return in; } volatile struct s_nunstruct nunstruct[6]; char * nunInterruptc[6] = { NULL }; bool nunfoundc[6] = { false }; unsigned char classicread = 0, nunchuckread = 0; /******************************************************************************************* I2C related commands in MMBasic =============================== These are the functions responsible for executing the I2C related commands in MMBasic They are supported by utility functions that are grouped at the end of this file ********************************************************************************************/ #ifdef PICOCALC void I2C_Send_RegData(int i2caddr,int reg,char command){ int i2cret; I2C_Send_Buffer[0]=reg; I2C_Send_Buffer[1]=command; I2C_Sendlen=2; I2C_Timeout=1000; if(I2C1locked)i2cret=i2c_write_timeout_us(i2c1, (uint8_t)i2caddr, (uint8_t *)I2C_Send_Buffer, I2C_Sendlen,false, I2C_Timeout*1000); else i2cret=i2c_write_timeout_us(i2c0, (uint8_t)i2caddr, (uint8_t *)I2C_Send_Buffer, I2C_Sendlen,false, I2C_Timeout*1000); mmI2Cvalue=0; if(i2cret==PICO_ERROR_GENERIC)mmI2Cvalue=1; if(i2cret==PICO_ERROR_TIMEOUT)mmI2Cvalue=2; // mmI2Cvalue=HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)i2caddr, I2C_Send_Buffer, I2C_Sendlen, I2C_Timeout); } #endif void I2C_Send_Command(char command) { int i2cret; int i2caddr = SSD1306_I2C_Addr; I2C_Send_Buffer[0] = 0; I2C_Send_Buffer[1] = command; I2C_Sendlen = 2; I2C_Timeout = 1000; if (I2C1locked) i2cret = i2c_write_timeout_us(i2c1, (uint8_t) i2caddr, (uint8_t * ) I2C_Send_Buffer, I2C_Sendlen, false, I2C_Timeout * 1000); else i2cret = i2c_write_timeout_us(i2c0, (uint8_t) i2caddr, (uint8_t * ) I2C_Send_Buffer, I2C_Sendlen, false, I2C_Timeout * 1000); mmI2Cvalue = 0; if (i2cret == PICO_ERROR_GENERIC) mmI2Cvalue = 1; if (i2cret == PICO_ERROR_TIMEOUT) mmI2Cvalue = 2; // mmI2Cvalue=HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)i2caddr, I2C_Send_Buffer, I2C_Sendlen, I2C_Timeout); } void I2C_Send_Data(unsigned char * data, int n) { int i2cret; int i, i2caddr = SSD1306_I2C_Addr; I2C_Sendlen = n + 1; I2C_Send_Buffer[0] = 0x40; I2C_Timeout = 1000; for (i = 1; i <= n; i++) { I2C_Send_Buffer[i] = data[i - 1]; } if (I2C1locked) i2cret = i2c_write_timeout_us(i2c1, (uint8_t) i2caddr, (uint8_t * ) I2C_Send_Buffer, I2C_Sendlen, false, I2C_Timeout * 1000); else i2cret = i2c_write_timeout_us(i2c0, (uint8_t) i2caddr, (uint8_t * ) I2C_Send_Buffer, I2C_Sendlen, false, I2C_Timeout * 1000); mmI2Cvalue = 0; if (i2cret == PICO_ERROR_GENERIC) mmI2Cvalue = 1; if (i2cret == PICO_ERROR_TIMEOUT) mmI2Cvalue = 2; } #ifndef PICOMITEVGA void ConfigDisplayI2C(unsigned char * p) { unsigned char DISPLAY_TYPE = 0; getargs( & p, 5, (unsigned char * ) ","); if (!(argc == 3 || argc == 5)) error("Argument count"); if (checkstring(argv[0], (unsigned char * ) "SSD1306I2C")) { DISPLAY_TYPE = SSD1306I2C; } else if (checkstring(argv[0], (unsigned char * ) "SSD1306I2C32")) { DISPLAY_TYPE = SSD1306I2C32; } else error("Invalid display type"); if (checkstring(argv[2], (unsigned char * ) "L") || checkstring(argv[2], (unsigned char * ) "LANDSCAPE")) Option.DISPLAY_ORIENTATION = LANDSCAPE; else if (checkstring(argv[2], (unsigned char * ) "P") || checkstring(argv[2], (unsigned char * ) "PORTRAIT")) Option.DISPLAY_ORIENTATION = PORTRAIT; else if (checkstring(argv[2], (unsigned char * ) "RL") || checkstring(argv[2], (unsigned char * ) "RLANDSCAPE")) Option.DISPLAY_ORIENTATION = RLANDSCAPE; else if (checkstring(argv[2], (unsigned char * ) "RP") || checkstring(argv[2], (unsigned char * ) "RPORTRAIT")) Option.DISPLAY_ORIENTATION = RPORTRAIT; else error("Orientation"); Option.I2Coffset = 0; if (argc == 5) Option.I2Coffset = getint(argv[4], 0, 10); if (!(I2C0locked || I2C1locked)) error("SYSTEM I2C not configured"); Option.Refresh = 1; Option.DISPLAY_TYPE = DISPLAY_TYPE; } void InitDisplayI2C(int InitOnly) { if (Option.DISPLAY_TYPE == 0 || Option.DISPLAY_TYPE > I2C_PANEL) return; // I2Con(); // i2c_enable(display_details[Option.DISPLAY_TYPE].speed); DrawRectangle = DrawRectangleMEM; DrawBitmap = DrawBitmapMEM; DrawBuffer = DrawBufferMEM; ReadBuffer = ReadBufferMEM; DrawPixel = DrawPixelMEM; DrawBLITBuffer = DrawBufferMEM; ReadBLITBuffer = ReadBufferMEM; DisplayHRes = display_details[Option.DISPLAY_TYPE].horizontal; DisplayVRes = display_details[Option.DISPLAY_TYPE].vertical; I2C_Send_Command(0xAE); //DISPLAYOFF I2C_Send_Command(0xD5); //DISPLAYCLOCKDIV I2C_Send_Command(0xF0); //the suggested ratio &H80 I2C_Send_Command(0xA8); //MULTIPLEX if (Option.DISPLAY_TYPE == SSD1306I2C) I2C_Send_Command(0x3F); else if (Option.DISPLAY_TYPE == SSD1306I2C32) I2C_Send_Command(0x1F); I2C_Send_Command(0xD3); //DISPLAYOFFSET I2C_Send_Command(0x0); //no offset I2C_Send_Command(0x40); //STARTLINE I2C_Send_Command(0x8D); //CHARGEPUMP I2C_Send_Command(0x14); I2C_Send_Command(0x20); //MEMORYMODE I2C_Send_Command(0x00); //&H0 act like ks0108 I2C_Send_Command(0xA1); //SEGREMAP OR 1 I2C_Send_Command(0xC8); //COMSCANDEC I2C_Send_Command(0xDA); //COMPINS if (Option.DISPLAY_TYPE == SSD1306I2C) I2C_Send_Command(0x12); else if (Option.DISPLAY_TYPE == SSD1306I2C32) I2C_Send_Command(0x02); I2C_Send_Command(0x81); //SETCONTRAST I2C_Send_Command(0xCF); I2C_Send_Command(0xd9); //SETPRECHARGE I2C_Send_Command(0x22); I2C_Send_Command(0xDB); //VCOMDETECT I2C_Send_Command(0x20); I2C_Send_Command(0xA4); //DISPLAYALLON_RESUME I2C_Send_Command(0xA6); //NORMALDISPLAY I2C_Send_Command(0xAF); //DISPLAYON if (Option.DISPLAY_ORIENTATION & 1) { VRes = DisplayVRes; HRes = DisplayHRes; } else { VRes = DisplayHRes; HRes = DisplayVRes; } if (!InitOnly) { ResetDisplay(); ClearScreen(0); Display_Refresh(); } } #endif /* @endcond */ void cmd_i2c(void) { unsigned char * p; //, *pp; if (I2C0SDApin == 99 || I2C0SCLpin == 99) error("Pin not set for I2C"); if ((p = checkstring(cmdline, (unsigned char * ) "OPEN")) != NULL) i2cEnable(p); else if ((p = checkstring(cmdline, (unsigned char * ) "CLOSE")) != NULL) i2cDisable(p); else if ((p = checkstring(cmdline, (unsigned char * ) "WRITE")) != NULL) i2cSend(p); else if ((p = checkstring(cmdline, (unsigned char * ) "READ")) != NULL) i2cReceive(p); else if ((p = checkstring(cmdline, (unsigned char * ) "CHECK")) != NULL) i2cCheck(p); else if ((p = checkstring(cmdline, (unsigned char * ) "SLAVE OPEN")) != NULL) i2cSlave(p); else if ((p = checkstring(cmdline, (unsigned char * ) "SLAVE READ")) != NULL) i2cReceiveSlave(p, 0); else if ((p = checkstring(cmdline, (unsigned char * ) "SLAVE WRITE")) != NULL) i2cSendSlave(p, 0); else if ((p = checkstring(cmdline, (unsigned char * ) "SLAVE CLOSE")) != NULL) i2cDisable(p); else error("Unknown command"); } void cmd_i2c2(void) { unsigned char * p; //, *pp; if (I2C1SDApin == 99 || I2C1SCLpin == 99) error("Pin not set for I2C2"); if ((p = checkstring(cmdline, (unsigned char * ) "OPEN")) != NULL) i2c2Enable(p); else if ((p = checkstring(cmdline, (unsigned char * ) "CLOSE")) != NULL) i2c2Disable(p); else if ((p = checkstring(cmdline, (unsigned char * ) "WRITE")) != NULL) i2c2Send(p); else if ((p = checkstring(cmdline, (unsigned char * ) "READ")) != NULL) i2c2Receive(p); else if ((p = checkstring(cmdline, (unsigned char * ) "CHECK")) != NULL) i2c2Check(p); else if ((p = checkstring(cmdline, (unsigned char * ) "SLAVE OPEN")) != NULL) i2c2Slave(p); else if ((p = checkstring(cmdline, (unsigned char * ) "SLAVE READ")) != NULL) i2cReceiveSlave(p, 1); else if ((p = checkstring(cmdline, (unsigned char * ) "SLAVE WRITE")) != NULL) i2cSendSlave(p, 1); else if ((p = checkstring(cmdline, (unsigned char * ) "SLAVE CLOSE")) != NULL) i2c2Disable(p); else error("Unknown command"); } /* * @cond * The following section will be excluded from the documentation. */ void __not_in_flash_func(i2c0_irq_handler)(void) { // Get interrupt status uint32_t status = i2c0 -> hw -> intr_stat; //is a write request? Or a read request ? event if (status & I2C_IC_INTR_STAT_R_RX_FULL_BITS) { i2c0 -> hw -> intr_mask = I2C_IC_INTR_MASK_M_RD_REQ_BITS; I2C_Status |= I2C_Status_Slave_Receive_Rdy; } else if (status & I2C_IC_INTR_STAT_R_RD_REQ_BITS) { i2c0 -> hw -> clr_rd_req; I2C_Status |= I2C_Status_Slave_Send_Rdy; } } void __not_in_flash_func(i2c1_irq_handler)(void) { // Get interrupt status uint32_t status = i2c1 -> hw -> intr_stat; //is a write request? Or a read request ? event if (status & I2C_IC_INTR_STAT_R_RX_FULL_BITS) { i2c1 -> hw -> intr_mask = I2C_IC_INTR_MASK_M_RD_REQ_BITS; I2C2_Status |= I2C_Status_Slave_Receive_Rdy; } else if (status & I2C_IC_INTR_STAT_R_RD_REQ_BITS) { i2c1 -> hw -> clr_rd_req; I2C2_Status |= I2C_Status_Slave_Send_Rdy; } } void i2cSlave(unsigned char * p) { int addr; getargs( & p, 5, (unsigned char * ) ","); if (argc != 5) error("Argument count"); if (I2C_Status & I2C_Status_Slave) error("Slave already open"); addr = getinteger(argv[0]); ExtCfg(I2C0SDApin, EXT_COM_RESERVED, 0); ExtCfg(I2C0SCLpin, EXT_COM_RESERVED, 0); gpio_pull_up(PinDef[I2C0SDApin].GPno); gpio_pull_up(PinDef[I2C0SCLpin].GPno); i2c_init(i2c0, 400000); I2C_Slave_Addr = addr; I2C_Slave_Send_IntLine = (char * ) GetIntAddress(argv[2]); // get the interrupt routine's location I2C_Slave_Receive_IntLine = (char * ) GetIntAddress(argv[4]); // get the interrupt routine's location InterruptUsed = true; i2c_set_slave_mode(i2c0, true, I2C_Slave_Addr); // Enable the I2C interrupts we want to process i2c0 -> hw -> intr_mask = I2C_IC_INTR_STAT_R_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS; // Set up the interrupt handler to service I2C interrupts irq_set_exclusive_handler(I2C0_IRQ, i2c0_irq_handler); irq_set_enabled(I2C0_IRQ, true); I2C_Status = I2C_Status_Slave; } void i2c2Slave(unsigned char * p) { int addr; getargs( & p, 5, (unsigned char * ) ","); if (argc != 5) error("Argument count"); if (I2C2_Status & I2C_Status_Slave) error("Slave already open"); addr = getinteger(argv[0]); ExtCfg(I2C1SDApin, EXT_COM_RESERVED, 0); ExtCfg(I2C1SCLpin, EXT_COM_RESERVED, 0); gpio_pull_up(PinDef[I2C1SDApin].GPno); gpio_pull_up(PinDef[I2C1SCLpin].GPno); i2c_init(i2c1, 400000); I2C2_Slave_Addr = addr; I2C2_Slave_Send_IntLine = (char * ) GetIntAddress(argv[2]); // get the interrupt routine's location I2C2_Slave_Receive_IntLine = (char * ) GetIntAddress(argv[4]); // get the interrupt routine's location InterruptUsed = true; i2c_set_slave_mode(i2c1, true, I2C2_Slave_Addr); // Enable the I2C interrupts we want to process i2c1 -> hw -> intr_mask = I2C_IC_INTR_STAT_R_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS; // Set up the interrupt handler to service I2C interrupts irq_set_exclusive_handler(I2C1_IRQ, i2c1_irq_handler); irq_set_enabled(I2C1_IRQ, true); I2C2_Status = I2C_Status_Slave; } int DoRtcI2C(int addr, unsigned char * buff) { if (I2C0locked) { I2C_Addr = addr; // address of the device i2c_masterCommand(1, buff); } else { I2C2_Addr = addr; // address of the device i2c2_masterCommand(1, buff); } return !mmI2Cvalue; } #ifndef USBKEYBOARD void CheckI2CKeyboard(int noerror, int read) { uint16_t buff; // int readover=0; static int ctrlheld = 0; // while(readover==0){ if (I2C0locked) { if (read == 0) { I2C_Sendlen = 1; // send one byte I2C_Rcvlen = 0; I2C_Status = 0; I2C_Send_Buffer[0] = 9; // the first register to read if (!(DoRtcI2C(0x1F, NULL))) goto i2c_error_exit; } else { I2C_Rcvbuf_String = (char * ) & buff; // we want a string of bytes I2C_Rcvbuf_Float = NULL; I2C_Rcvbuf_Int = NULL; I2C_Rcvlen = 2; // get 7 bytes I2C_Sendlen = 0; if (!DoRtcI2C(0x1F, (unsigned char * ) & buff)) goto i2c_error_exit; } } else { I2C2_Sendlen = 1; // send one byte I2C2_Rcvlen = 0; I2C2_Status = 0; I2C_Send_Buffer[0] = 9; // the first register to read if (!(DoRtcI2C(0x1F, NULL))) goto i2c_error_exit; I2C2_Rcvbuf_String = (char * ) & buff; // we want a string of bytes I2C2_Rcvbuf_Float = NULL; I2C2_Rcvbuf_Int = NULL; I2C2_Rcvlen = 2; // get 7 bytes I2C2_Sendlen = 0; if (!DoRtcI2C(0x1F, (unsigned char * ) & buff)) goto i2c_error_exit; } uSec(1000); if (buff) { #ifdef PICOCALC if (buff == 0xA503) ctrlheld = 0; else if (buff == 0xA502) { ctrlheld=1; } else if((buff & 0xff) == 1) { //pressed int c = buff >> 8; int realc = 0; switch(c) { // Refer to Appendix H in PicoMite User Manual // PicoCalc must be mapped to expected PicoMite keys case 0xd4: realc=DEL; break; case 0xb5: realc=UP; break; case 0xb6: realc=DOWN; break; case 0xb4: realc=LEFT; break; case 0xb7: realc=RIGHT; break; case 0xd1: realc=INSERT; break; // ALT + I case 0xd2: realc=HOME; break; // SHIFT + TAB (collision, see below) case 0xd5: realc=END; break; // SHIFT + DEL (collision, see below) case 0xd6: realc=PUP; break; // SHIFT + UP case 0xd7: realc=PDOWN; break; // SHIFT + DOWN (collision, see below) case 0xa1: realc=ALT; break; // Note: SHIFT + ENTER also sends ALT! case 0x81: realc=F1; break; case 0x82: realc=F2; break; case 0x83: realc=F3; break; case 0x84: realc=F4; break; case 0x85: realc=F5; break; case 0x86: realc=F6; break; // SHIFT + F1 case 0x87: realc=F7; break; // SHIFT + F2 case 0x88: realc=F8; break; // SHIFT + F3 case 0x89: realc=F9; break; // SHIFT + F4 case 0x90: realc=F10; break; // SHIFT + F5 // F11 not on PicoCalc // F12 not on PicoCalc // PrtScr/SysRq not on PicoCalc case 0xd0: realc=BreakKey; break; // SHIFT_TAB sends Home on PicoCalc // SHIFT_DEL sends End on PicoCalc // DOWNSEL (SHIFT_DOWN_ARROW) sends Page Down (PDOWN) on PicoCalc // Note: (SHIFT_UP_ARROW) sends Page Up (PUP) on PicoCalc // RIGHTSEL (SHIFT_RIGHT_ARROW) sends nothing on PicoCalc // Note: (SHIFT_LEFT_ARROW) sends nothing on PicoCalc // Note: PicoCalc cannot send shifted Fn keys! // --- Appendix H ends case 0xb1: realc=ESC; break; case 0x0a: realc=ENTER; break; // --- Modifier keys must be consumed and ignored! case 0xa2: // Shift (left) case 0xa3: // Shift (right) case 0xa5: // Ctrl case 0xc1: // CapsLK return; default: realc = c; break; } c = realc; #else if (buff == 0x1203) ctrlheld = 0; else if (buff == 0x1202) { ctrlheld = 1; } else if ((buff & 0xff) == 1) { int c = buff >> 8; if (c == 6) c = ESC; if (c == 0x11) c = F1; if (c == 5) c = F2; if (c == 0x7) c = F4; #endif if (c >= 'a' && c <= 'z' && ctrlheld) c = c - 'a' + 1; if (c == BreakKey) { // if the user wants to stop the progran MMAbort = true; // set the flag for the interpreter to see ConsoleRxBufHead = ConsoleRxBufTail; // empty the buffer // break; } else { ConsoleRxBuf[ConsoleRxBufHead] = c; // store the byte in the ring buffer if (ConsoleRxBuf[ConsoleRxBufHead] == keyselect && KeyInterrupt != NULL) { Keycomplete = true; } else { ConsoleRxBufHead = (ConsoleRxBufHead + 1) % CONSOLE_RX_BUF_SIZE; // advance the head of the queue if (ConsoleRxBufHead == ConsoleRxBufTail) { // if the buffer has overflowed ConsoleRxBufTail = (ConsoleRxBufTail + 1) % CONSOLE_RX_BUF_SIZE; // throw away the oldest char } } } } // } else readover=1; } return; i2c_error_exit: if (noerror) { noI2C = 1; return; } if (CurrentLinePtr) error("I2C Keyboard not responding"); if (Option.KeyboardConfig == CONFIG_I2C) { MMPrintString("I2C Keyboard not responding"); MMPrintString("\r\n"); } } #endif void RtcGetTime(int noerror) { char * buff = GetTempMemory(STRINGSIZE); // Received data is stored here int DS1307; if (I2C0locked) { I2C_Sendlen = 1; // send one byte I2C_Rcvlen = 0; I2C_Status = 0; I2C_Send_Buffer[0] = 0; // the first register to read if (!(DS1307 = DoRtcI2C(0x68, NULL))) { I2C_Send_Buffer[0] = 2; // the first register is different for the PCF8563 if (!DoRtcI2C(0x51, NULL)) goto error_exit; } I2C_Rcvbuf_String = buff; // we want a string of bytes I2C_Rcvbuf_Float = NULL; I2C_Rcvbuf_Int = NULL; I2C_Rcvlen = 7; // get 7 bytes I2C_Sendlen = 0; if (!DoRtcI2C(DS1307 ? 0x68 : 0x51, (unsigned char * ) buff)) goto error_exit; } else { I2C2_Sendlen = 1; // send one byte I2C2_Rcvlen = 0; I2C2_Status = 0; I2C_Send_Buffer[0] = 0; // the first register to read if (!(DS1307 = DoRtcI2C(0x68, NULL))) { I2C_Send_Buffer[0] = 2; // the first register is different for the PCF8563 if (!DoRtcI2C(0x51, NULL)) goto error_exit; } I2C2_Rcvbuf_String = buff; // we want a string of bytes I2C2_Rcvbuf_Float = NULL; I2C2_Rcvbuf_Int = NULL; I2C2_Rcvlen = 7; // get 7 bytes I2C2_Sendlen = 0; if (!DoRtcI2C(DS1307 ? 0x68 : 0x51, (unsigned char * ) buff)) goto error_exit; } // mT4IntEnable(0); int year, month, day, hour, minute, second; second = ((buff[0] & 0x7f) >> 4) * 10 + (buff[0] & 0x0f); minute = ((buff[1] & 0x7f) >> 4) * 10 + (buff[1] & 0x0f); hour = ((buff[2] & 0x3f) >> 4) * 10 + (buff[2] & 0x0f); day = ((buff[DS1307 ? 4 : 3] & 0x3f) >> 4) * 10 + (buff[DS1307 ? 4 : 3] & 0x0f); month = ((buff[5] & 0x1f) >> 4) * 10 + (buff[5] & 0x0f); year = (buff[6] >> 4) * 10 + (buff[6] & 0x0f) + 2000; // mT4IntEnable(1); TimeOffsetToUptime = get_epoch(year, month, day, hour, minute, second) - time_us_64() / 1000000; return; error_exit: if (noerror) { noRTC = 1; return; } if (CurrentLinePtr) error("RTC not responding"); if (Option.RTC) { MMPrintString("RTC not responding"); MMPrintString("\r\n"); } } // universal function to send/receive data to/from the RTC // addr is the I2C address WITHOUT the read/write bit char CvtToBCD(unsigned char * p, int min, int max) { long long int t; t = getint(p, min, max) % 100; return ((t / 10) << 4) | (t % 10); } char CvtCharsToBCD(unsigned char * p, int min, int max) { int t; t = (p[0] - '0') * 10 + (p[1] - '0'); // dp("|%c| |%c| %d %d %d", p[0], p[1], t, min, max); if (!isdigit(p[0]) || !isdigit(p[1]) || t < min || t > max) error("Date/time format"); return ((t / 10) << 4) | (t % 10); } /* @endcond */ void MIPS16 cmd_rtc(void) { char buff[7]; // Received data is stored here int DS1307; unsigned char * p; void * ptr = NULL; if (!(I2C0locked || I2C1locked)) error("SYSTEM I2C not configured"); if (checkstring(cmdline, (unsigned char * ) "GETTIME")) { RtcGetTime(0); return; } if ((p = checkstring(cmdline, (unsigned char * ) "SETTIME")) != NULL) { int Fulldate = 0; getargs( & p, 11, (unsigned char * ) ","); if (I2C0locked) { if (argc == 1) { // single argument - assume the data is in DATETIME2 format used by GUI FORMATBOX p = getCstring(argv[0]); if (!(p[2] == '/' || p[2] == '-') || !(p[11] == ':' || p[13] == ':')) error("Date/time format"); if (p[13] == ':') Fulldate = 2; if (p[14 + Fulldate] == ':') I2C_Send_Buffer[1] = CvtCharsToBCD(p + 15 + Fulldate, 0, 59); // seconds else I2C_Send_Buffer[1] = 0; // seconds defaults to zero I2C_Send_Buffer[2] = CvtCharsToBCD(p + 12 + Fulldate, 0, 59); // minutes I2C_Send_Buffer[3] = CvtCharsToBCD(p + 9 + Fulldate, 0, 23); // hour I2C_Send_Buffer[5] = CvtCharsToBCD(p, 1, 31); // day I2C_Send_Buffer[6] = CvtCharsToBCD(p + 3, 1, 12); // month I2C_Send_Buffer[7] = CvtCharsToBCD(p + 6 + Fulldate, 0, 99); // year } else { // multiple arguments - data should be in the original yy, mm, dd, etc format if (argc != 11) error("Argument count"); I2C_Send_Buffer[1] = CvtToBCD(argv[10], 0, 59); // seconds I2C_Send_Buffer[2] = CvtToBCD(argv[8], 0, 59); // minutes I2C_Send_Buffer[3] = CvtToBCD(argv[6], 0, 23); // hour I2C_Send_Buffer[5] = CvtToBCD(argv[4], 1, 31); // day I2C_Send_Buffer[6] = CvtToBCD(argv[2], 1, 12); // month I2C_Send_Buffer[7] = CvtToBCD(argv[0], 0, 2099); // year } I2C_Send_Buffer[0] = 0; // turn off the square wave I2C_Send_Buffer[4] = 1; I2C_Rcvlen = 0; I2C_Sendlen = 9; // send 7 bytes if (!DoRtcI2C(0x68, NULL)) { I2C_Send_Buffer[9] = I2C_Send_Buffer[7]; // year I2C_Send_Buffer[8] = I2C_Send_Buffer[6]; // month I2C_Send_Buffer[7] = 1; I2C_Send_Buffer[6] = I2C_Send_Buffer[5]; // day I2C_Send_Buffer[5] = I2C_Send_Buffer[3]; // hour I2C_Send_Buffer[4] = I2C_Send_Buffer[2]; // minutes I2C_Send_Buffer[3] = I2C_Send_Buffer[1]; // seconds I2C_Send_Buffer[0] = I2C_Send_Buffer[1] = I2C_Send_Buffer[2] = 0; // set the register pointer to the first register then zero the first two registers I2C_Sendlen = 10; // send 10 bytes if (!DoRtcI2C(0x51, NULL)) error("RTC not responding"); } } else { if (argc == 1) { // single argument - assume the data is in DATETIME2 format used by GUI FORMATBOX p = getCstring(argv[0]); if (!(p[2] == '/' || p[2] == '-') || !(p[11] == ':' || p[13] == ':')) error("Date/time format"); if (p[13] == ':') Fulldate = 2; if (p[14 + Fulldate] == ':') I2C_Send_Buffer[1] = CvtCharsToBCD(p + 15 + Fulldate, 0, 59); // seconds else I2C_Send_Buffer[1] = 0; // seconds defaults to zero I2C_Send_Buffer[2] = CvtCharsToBCD(p + 12 + Fulldate, 0, 59); // minutes I2C_Send_Buffer[3] = CvtCharsToBCD(p + 9 + Fulldate, 0, 23); // hour I2C_Send_Buffer[5] = CvtCharsToBCD(p, 1, 31); // day I2C_Send_Buffer[6] = CvtCharsToBCD(p + 3, 1, 12); // month I2C_Send_Buffer[7] = CvtCharsToBCD(p + 6 + Fulldate, 0, 99); // year } else { // multiple arguments - data should be in the original yy, mm, dd, etc format if (argc != 11) error("Argument count"); I2C_Send_Buffer[1] = CvtToBCD(argv[10], 0, 59); // seconds I2C_Send_Buffer[2] = CvtToBCD(argv[8], 0, 59); // minutes I2C_Send_Buffer[3] = CvtToBCD(argv[6], 0, 23); // hour I2C_Send_Buffer[5] = CvtToBCD(argv[4], 1, 31); // day I2C_Send_Buffer[6] = CvtToBCD(argv[2], 1, 12); // month I2C_Send_Buffer[7] = CvtToBCD(argv[0], 0, 2099); // year } I2C_Send_Buffer[0] = 0; // turn off the square wave I2C_Send_Buffer[4] = 1; I2C2_Rcvlen = 0; I2C2_Sendlen = 9; // send 7 bytes if (!DoRtcI2C(0x68, NULL)) { I2C_Send_Buffer[9] = I2C_Send_Buffer[7]; // year I2C_Send_Buffer[8] = I2C_Send_Buffer[6]; // month I2C_Send_Buffer[7] = 1; I2C_Send_Buffer[6] = I2C_Send_Buffer[5]; // day I2C_Send_Buffer[5] = I2C_Send_Buffer[3]; // hour I2C_Send_Buffer[4] = I2C_Send_Buffer[2]; // minutes I2C_Send_Buffer[3] = I2C_Send_Buffer[1]; // seconds I2C_Send_Buffer[0] = I2C_Send_Buffer[1] = I2C_Send_Buffer[2] = 0; // set the register pointer to the first register then zero the first two registers I2C2_Sendlen = 10; // send 10 bytes if (!DoRtcI2C(0x51, NULL)) error("RTC not responding"); } } RtcGetTime(0); } else if ((p = checkstring(cmdline, (unsigned char * ) "GETREG")) != NULL) { getargs( & p, 3, (unsigned char * ) ","); if (argc != 3) error("Argument count"); if (I2C0locked) { I2C_Sendlen = 1; // send one byte I2C_Rcvlen = 0; * I2C_Send_Buffer = getint(argv[0], 0, 255); // the register to read } else { I2C2_Sendlen = 1; // send one byte I2C2_Rcvlen = 0; * I2C_Send_Buffer = getint(argv[0], 0, 255); // the register to read } ptr = findvar(argv[2], V_FIND); if (g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); if (g_vartbl[g_VarIndex].type & T_STR) error("Invalid variable"); if (!(DS1307 = DoRtcI2C(0x68, NULL))) { if (!DoRtcI2C(0x51, NULL)) error("RTC not responding"); } if (I2C0locked) { I2C_Rcvbuf_String = buff; // we want a string of bytes I2C_Rcvbuf_Float = NULL; I2C_Rcvbuf_Int = NULL; I2C_Rcvlen = 1; // get 1 byte I2C_Sendlen = 0; } else { I2C2_Rcvbuf_String = buff; // we want a string of bytes I2C2_Rcvbuf_Float = NULL; I2C2_Rcvbuf_Int = NULL; I2C2_Rcvlen = 1; // get 1 byte I2C2_Sendlen = 0; } if (!DoRtcI2C(DS1307 ? 0x68 : 0x51, (unsigned char * ) buff)) error("RTC not responding1"); if (g_vartbl[g_VarIndex].type & T_NBR) * (MMFLOAT * ) ptr = buff[0]; else * (long long int * ) ptr = buff[0]; } else if ((p = checkstring(cmdline, (unsigned char * ) "SETREG")) != NULL) { getargs( & p, 3, (unsigned char * ) ","); if (argc != 3) error("Argument count"); if (I2C0locked) { I2C_Rcvlen = 0; I2C_Send_Buffer[0] = getint(argv[0], 0, 255); // set the register pointer I2C_Send_Buffer[1] = getint(argv[2], 0, 255); // and the data to be written I2C_Sendlen = 2; // send 2 bytes } else { I2C2_Rcvlen = 0; I2C_Send_Buffer[0] = getint(argv[0], 0, 255); // set the register pointer I2C_Send_Buffer[1] = getint(argv[2], 0, 255); // and the data to be written I2C2_Sendlen = 2; // send 2 bytes } if (!DoRtcI2C(0x68, NULL)) { if (!DoRtcI2C(0x51, NULL)) error("RTC not responding"); } } else error("Unknown command"); } /* * @cond * The following section will be excluded from the documentation. */ // enable the I2C1 module - master mode void i2cEnable(unsigned char * p) { int speed, timeout; getargs( & p, 3, (unsigned char * ) ","); if (argc != 3) error("Invalid syntax"); speed = getinteger(argv[0]); if (!(speed == 100 || speed == 400 || speed == 1000)) error("Valid speeds 100, 400, 1000"); timeout = getinteger(argv[2]); if (timeout < 0 || (timeout > 0 && timeout < 100)) error("Number out of bounds"); if (I2C_enabled || I2C_Status & I2C_Status_Slave) error("I2C already OPEN"); I2C_Timeout = timeout; i2c_enable(speed); } // enable the I2C1 module - master mode void i2c2Enable(unsigned char * p) { int speed, timeout; getargs( & p, 3, (unsigned char * ) ","); if (argc != 3) error("Invalid syntax"); speed = getinteger(argv[0]); if (!(speed == 100 || speed == 400 || speed == 1000)) error("Valid speeds 100, 400, 1000"); timeout = getinteger(argv[2]); if (timeout < 0 || (timeout > 0 && timeout < 100)) error("Number out of bounds"); if (I2C2_enabled || I2C2_Status & I2C_Status_Slave) error("I2C already OPEN"); I2C2_Timeout = timeout; i2c2_enable(speed); } // disable the I2C1 module - master mode void i2cDisable(unsigned char * p) { if (!I2C0locked) i2c_disable(); else error("Allocated to System I2C"); } // disable the I2C1 module - master mode void i2c2Disable(unsigned char * p) { if (!I2C1locked) i2c2_disable(); else error("Allocated to System I2C"); } // send data to an I2C slave - master mode void i2cSend(unsigned char * p) { int addr, i2c_options, sendlen, i; void * ptr = NULL; unsigned char * cptr = NULL; getargs( & p, 99, (unsigned char * ) ","); if (!(argc & 0x01) || (argc < 7)) error("Invalid syntax"); if (!I2C_enabled) error("I2C not open"); addr = getinteger(argv[0]); i2c_options = getinteger(argv[2]); if (i2c_options < 0 || i2c_options > 3) error("Number out of bounds"); I2C_Status = 0; if (i2c_options & 0x01) I2C_Status = I2C_Status_BusHold; I2C_Addr = addr; sendlen = getint(argv[4], 1, 256); if (sendlen == 1 || argc > 7) { // numeric expressions for data if (sendlen != ((argc - 5) >> 1)) error("Incorrect argument count"); for (i = 0; i < sendlen; i++) { I2C_Send_Buffer[i] = getinteger(argv[i + i + 6]); } } else { // an array of MMFLOAT, integer or a string ptr = findvar(argv[6], V_NOFIND_NULL | V_EMPTY_OK); if (ptr == NULL) error("Invalid variable"); if ((g_vartbl[g_VarIndex].type & T_STR) && g_vartbl[g_VarIndex].dims[0] == 0) { // string if (sendlen > 255) error("Number out of bounds"); cptr = (unsigned char * ) ptr; cptr++; // skip the length byte in a MMBasic string for (i = 0; i < sendlen; i++) { I2C_Send_Buffer[i] = (int)( * (cptr + i)); } } else if ((g_vartbl[g_VarIndex].type & T_NBR) && g_vartbl[g_VarIndex].dims[0] > 0 && g_vartbl[g_VarIndex].dims[1] == 0) { // numeric array if ((((MMFLOAT * ) ptr - g_vartbl[g_VarIndex].val.fa) + sendlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) { error("Insufficient data"); } else { for (i = 0; i < sendlen; i++) { I2C_Send_Buffer[i] = (int)( * ((MMFLOAT * ) ptr + i)); } } } else if ((g_vartbl[g_VarIndex].type & T_INT) && g_vartbl[g_VarIndex].dims[0] > 0 && g_vartbl[g_VarIndex].dims[1] == 0) { // integer array if ((((long long int * ) ptr - g_vartbl[g_VarIndex].val.ia) + sendlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) { error("Insufficient data"); } else { for (i = 0; i < sendlen; i++) { I2C_Send_Buffer[i] = (int)( * ((long long int * ) ptr + i)); } } } else error("Invalid variable"); } I2C_Sendlen = sendlen; I2C_Rcvlen = 0; i2c_masterCommand(1, NULL); } // send data to an I2C slave - master mode void i2cSendSlave(unsigned char * p, int channel) { int sendlen, i; void * ptr = NULL; unsigned char * cptr = NULL; getargs( & p, 99, (unsigned char * ) ","); if (!(argc >= 3)) error("Invalid syntax"); if (!((I2C_Status & I2C_Status_Slave && channel == 0) || (I2C2_Status & I2C_Status_Slave && channel == 1))) error("I2C slave not open"); unsigned char * bbuff; if (channel == 0) { bbuff = I2C_Send_Buffer; } else { bbuff = I2C_Send_Buffer; } sendlen = getinteger(argv[0]); if (sendlen < 1 || sendlen > 255) error("Number out of bounds"); if (sendlen == 1 || argc > 3) { // numeric expressions for data if (sendlen != ((argc - 1) >> 1)) error("Incorrect argument count"); for (i = 0; i < sendlen; i++) { bbuff[i] = getinteger(argv[i + i + 2]); } } else { // an array of MMFLOAT, integer or a string ptr = findvar(argv[2], V_NOFIND_NULL | V_EMPTY_OK); if (ptr == NULL) error("Invalid variable"); if ((g_vartbl[g_VarIndex].type & T_STR) && g_vartbl[g_VarIndex].dims[0] == 0) { // string cptr = (unsigned char * ) ptr; cptr++; // skip the length byte in a MMBasic string for (i = 0; i < sendlen; i++) { bbuff[i] = (int)( * (cptr + i)); } } else if ((g_vartbl[g_VarIndex].type & T_NBR) && g_vartbl[g_VarIndex].dims[0] > 0 && g_vartbl[g_VarIndex].dims[1] == 0) { // numeric array if ((((MMFLOAT * ) ptr - g_vartbl[g_VarIndex].val.fa) + sendlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) { error("Insufficient data"); } else { for (i = 0; i < sendlen; i++) { bbuff[i] = (int)( * ((MMFLOAT * ) ptr + i)); } } } else if ((g_vartbl[g_VarIndex].type & T_INT) && g_vartbl[g_VarIndex].dims[0] > 0 && g_vartbl[g_VarIndex].dims[1] == 0) { // integer array if ((((long long int * ) ptr - g_vartbl[g_VarIndex].val.ia) + sendlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) { error("Insufficient data"); } else { for (i = 0; i < sendlen; i++) { bbuff[i] = (int)( * ((long long int * ) ptr + i)); } } } else error("Invalid variable"); } if (channel == 0) i2c_write_raw_blocking(i2c0, bbuff, sendlen); else i2c_write_raw_blocking(i2c1, bbuff, sendlen); } // send data to an I2C slave - master mode void i2c2Send(unsigned char * p) { int addr, i2c2_options, sendlen, i; void * ptr = NULL; unsigned char * cptr = NULL; getargs( & p, 99, (unsigned char * ) ","); if (!(argc & 0x01) || (argc < 7)) error("Invalid syntax"); if (!I2C2_enabled) error("I2C not open"); addr = getinteger(argv[0]); i2c2_options = getinteger(argv[2]); if (i2c2_options < 0 || i2c2_options > 3) error("Number out of bounds"); I2C2_Status = 0; if (i2c2_options & 0x01) I2C2_Status = I2C_Status_BusHold; I2C2_Addr = addr; sendlen = getint(argv[4], 1, 256); if (sendlen == 1 || argc > 7) { // numeric expressions for data if (sendlen != ((argc - 5) >> 1)) error("Incorrect argument count"); for (i = 0; i < sendlen; i++) { I2C_Send_Buffer[i] = getinteger(argv[i + i + 6]); } } else { // an array of MMFLOAT, integer or a string ptr = findvar(argv[6], V_NOFIND_NULL | V_EMPTY_OK); if (ptr == NULL) error("Invalid variable"); if ((g_vartbl[g_VarIndex].type & T_STR) && g_vartbl[g_VarIndex].dims[0] == 0) { // string if (sendlen > 255) error("Number out of bounds"); cptr = (unsigned char * ) ptr; cptr++; // skip the length byte in a MMBasic string for (i = 0; i < sendlen; i++) { I2C_Send_Buffer[i] = (int)( * (cptr + i)); } } else if ((g_vartbl[g_VarIndex].type & T_NBR) && g_vartbl[g_VarIndex].dims[0] > 0 && g_vartbl[g_VarIndex].dims[1] == 0) { // numeric array if ((((MMFLOAT * ) ptr - g_vartbl[g_VarIndex].val.fa) + sendlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) { error("Insufficient data"); } else { for (i = 0; i < sendlen; i++) { I2C_Send_Buffer[i] = (int)( * ((MMFLOAT * ) ptr + i)); } } } else if ((g_vartbl[g_VarIndex].type & T_INT) && g_vartbl[g_VarIndex].dims[0] > 0 && g_vartbl[g_VarIndex].dims[1] == 0) { // integer array if ((((long long int * ) ptr - g_vartbl[g_VarIndex].val.ia) + sendlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) { error("Insufficient data"); } else { for (i = 0; i < sendlen; i++) { I2C_Send_Buffer[i] = (int)( * ((long long int * ) ptr + i)); } } } else error("Invalid variable"); } I2C2_Sendlen = sendlen; I2C2_Rcvlen = 0; i2c2_masterCommand(1, NULL); } void i2cCheck(unsigned char * p) { int addr; uint8_t rxdata; getargs( & p, 1, (unsigned char * ) ","); if (!I2C_enabled) error("I2C not open"); addr = getinteger(argv[0]); if (addr < 0 || addr > 0x7F) error("Invalid I2C address"); // int ret=i2c_read_blocking(i2c0, addr, &rxdata, 1, false); int ret = i2c_read_timeout_us(i2c0, addr, & rxdata, 1, false, 100); mmI2Cvalue = ret < 0 ? 1 : 0; } void i2c2Check(unsigned char * p) { int addr; uint8_t rxdata; getargs( & p, 1, (unsigned char * ) ","); if (!I2C2_enabled) error("I2C not open"); addr = getinteger(argv[0]); if (addr < 0 || addr > 0x7F) error("Invalid I2C address"); // int ret=i2c_read_blocking(i2c1, addr, &rxdata, 1, false); int ret = i2c_read_timeout_us(i2c1, addr, & rxdata, 1, false, 100); mmI2Cvalue = ret < 0 ? 1 : 0; } // receive data from an I2C slave - master mode void i2cReceive(unsigned char * p) { int addr, i2c_options, rcvlen; void * ptr = NULL; getargs( & p, 7, (unsigned char * ) ","); if (argc != 7) error("Invalid syntax"); if (!I2C_enabled) error("I2C not open"); addr = getinteger(argv[0]); i2c_options = getint(argv[2], 0, 1); I2C_Status = 0; I2C_Rcvbuf_Float = NULL; I2C_Rcvbuf_Int = NULL; I2C_Rcvbuf_String = NULL; if (i2c_options & 0x01) I2C_Status = I2C_Status_BusHold; I2C_Addr = addr; rcvlen = getinteger(argv[4]); if (rcvlen < 1) error("Number out of bounds"); ptr = findvar(argv[6], V_FIND | V_EMPTY_OK); if (g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); if (ptr == NULL) error("Invalid variable"); if (g_vartbl[g_VarIndex].type & T_NBR) { if (g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable"); if (g_vartbl[g_VarIndex].dims[0] <= 0) { // Not an array if (rcvlen != 1) error("Invalid variable"); } else { // An array if ((((MMFLOAT * ) ptr - g_vartbl[g_VarIndex].val.fa) + rcvlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) error("Insufficient space in array"); } I2C_Rcvbuf_Float = (MMFLOAT * ) ptr; } else if (g_vartbl[g_VarIndex].type & T_INT) { if (g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable"); if (g_vartbl[g_VarIndex].dims[0] <= 0) { // Not an array if (rcvlen != 1) error("Invalid variable"); } else { // An array if ((((long long int * ) ptr - g_vartbl[g_VarIndex].val.ia) + rcvlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) error("Insufficient space in array"); } I2C_Rcvbuf_Int = (long long int * ) ptr; } else if (g_vartbl[g_VarIndex].type & T_STR) { if (rcvlen < 1 || rcvlen > 255) error("Number out of bounds"); if (g_vartbl[g_VarIndex].dims[0] != 0) error("Invalid variable"); *(char * ) ptr = rcvlen; I2C_Rcvbuf_String = (char * ) ptr + 1; } else error("Invalid variable"); I2C_Rcvlen = rcvlen; I2C_Sendlen = 0; char * buff = GetTempMemory(rcvlen > 255 ? rcvlen + 2 : STRINGSIZE); // PInt((uint32_t)I2C_Rcvbuf_String); i2c_masterCommand(1, (unsigned char * ) buff); // PIntComma(rcvlen); // PInt((uint32_t)I2C_Rcvbuf_String);PRet(); // if(g_vartbl[g_VarIndex].type & T_STR)*(char *)ptr = rcvlen; } void i2cReceiveSlave(unsigned char * p, int channel) { int rcvlen; void * ptr = NULL; MMFLOAT * rcvdlenFloat = NULL; long long int * rcvdlenInt = NULL; int count = 1; I2C_Rcvbuf_Float = NULL; I2C_Rcvbuf_Int = NULL; I2C_Rcvbuf_String = NULL; I2C2_Rcvbuf_Float = NULL; I2C2_Rcvbuf_Int = NULL; I2C2_Rcvbuf_String = NULL; getargs( & p, 5, (unsigned char * ) ","); if (argc != 5) error("Invalid syntax"); if (!((I2C_Status & I2C_Status_Slave && channel == 0) || (I2C2_Status & I2C_Status_Slave && channel == 1))) error("I2C slave not open"); rcvlen = getinteger(argv[0]); if (rcvlen < 1 || rcvlen > 255) error("Number out of bounds"); ptr = findvar(argv[2], V_FIND | V_EMPTY_OK); if (g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); if (ptr == NULL) error("Invalid variable"); if (g_vartbl[g_VarIndex].type & T_NBR) { if (g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable"); if (g_vartbl[g_VarIndex].dims[0] <= 0) { // Not an array if (rcvlen != 1) error("Invalid variable"); } else { // An array if ((((MMFLOAT * ) ptr - g_vartbl[g_VarIndex].val.fa) + rcvlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) error("Insufficient space in array"); } I2C_Rcvbuf_Float = (MMFLOAT * ) ptr; } else if (g_vartbl[g_VarIndex].type & T_INT) { if (g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable"); if (g_vartbl[g_VarIndex].dims[0] <= 0) { // Not an array if (rcvlen != 1) error("Invalid variable"); } else { // An array if ((((long long int * ) ptr - g_vartbl[g_VarIndex].val.ia) + rcvlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) error("Insufficient space in array"); } I2C_Rcvbuf_Int = (long long int * ) ptr; } else if (g_vartbl[g_VarIndex].type & T_STR) { if (g_vartbl[g_VarIndex].dims[0] != 0) error("Invalid variable"); *(char * ) ptr = rcvlen; I2C_Rcvbuf_String = (char * ) ptr + 1; } else error("Invalid variable"); ptr = findvar(argv[4], V_FIND); if (g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); if (g_vartbl[g_VarIndex].type & T_NBR) rcvdlenFloat = (MMFLOAT * ) ptr; else if (g_vartbl[g_VarIndex].type & T_INT) rcvdlenInt = (long long int * ) ptr; else error("Invalid variable"); unsigned char * bbuff; if (channel == 0) { bbuff = I2C_Send_Buffer; i2c_read_raw_blocking(i2c0, bbuff, 1); if (rcvlen > 1) { I2CTimer = 0; while (count < rcvlen && I2CTimer < rcvlen / 10 + 2) { if (i2c0 -> hw -> status & 8) i2c_read_raw_blocking(i2c0, & bbuff[count++], 1); } } } else { bbuff = I2C_Send_Buffer; i2c_read_raw_blocking(i2c1, bbuff, 1); if (rcvlen > 1) { I2CTimer = 0; while (count < rcvlen && I2CTimer < rcvlen / 10 + 2) { if (i2c1 -> hw -> status & 8) i2c_read_raw_blocking(i2c1, & bbuff[count++], 1); } } } for (int i = 0; i < rcvlen; i++) { if (I2C_Rcvbuf_String != NULL) { * I2C_Rcvbuf_String = bbuff[i]; I2C_Rcvbuf_String++; } if (I2C_Rcvbuf_Float != NULL) { * I2C_Rcvbuf_Float = bbuff[i]; I2C_Rcvbuf_Float++; } if (I2C_Rcvbuf_Int != NULL) { * I2C_Rcvbuf_Int = bbuff[i]; I2C_Rcvbuf_Int++; } } if (!(rcvdlenFloat == NULL)) * rcvdlenFloat = (MMFLOAT) count; else * rcvdlenInt = (long long int) count; if (channel == 0) i2c0 -> hw -> intr_mask = I2C_IC_INTR_STAT_R_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS; else i2c1 -> hw -> intr_mask = I2C_IC_INTR_STAT_R_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS; } // receive data from an I2C slave - master mode void i2c2Receive(unsigned char * p) { int addr, i2c2_options, rcvlen; void * ptr = NULL; getargs( & p, 7, (unsigned char * ) ","); if (argc != 7) error("Invalid syntax"); if (!I2C2_enabled) error("I2C not open"); addr = getinteger(argv[0]); i2c2_options = getint(argv[2], 0, 1); I2C2_Status = 0; if (i2c2_options & 0x01) I2C2_Status = I2C_Status_BusHold; I2C2_Addr = addr; I2C2_Rcvbuf_Float = NULL; I2C2_Rcvbuf_Int = NULL; I2C2_Rcvbuf_String = NULL; rcvlen = getinteger(argv[4]); if (rcvlen < 1) error("Number out of bounds"); ptr = findvar(argv[6], V_FIND | V_EMPTY_OK); if (g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); if (ptr == NULL) error("Invalid variable"); if (g_vartbl[g_VarIndex].type & T_NBR) { if (g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable"); if (g_vartbl[g_VarIndex].dims[0] <= 0) { // Not an array if (rcvlen != 1) error("Invalid variable"); } else { // An array if ((((MMFLOAT * ) ptr - g_vartbl[g_VarIndex].val.fa) + rcvlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) error("Insufficient space in array"); } I2C2_Rcvbuf_Float = (MMFLOAT * ) ptr; } else if (g_vartbl[g_VarIndex].type & T_INT) { if (g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable"); if (g_vartbl[g_VarIndex].dims[0] <= 0) { // Not an array if (rcvlen != 1) error("Invalid variable"); } else { // An array if ((((long long int * ) ptr - g_vartbl[g_VarIndex].val.ia) + rcvlen) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)) error("Insufficient space in array"); } I2C2_Rcvbuf_Int = (long long int * ) ptr; } else if (g_vartbl[g_VarIndex].type & T_STR) { if (rcvlen < 1 || rcvlen > 255) error("Number out of bounds"); if (g_vartbl[g_VarIndex].dims[0] != 0) error("Invalid variable"); *(char * ) ptr = rcvlen; I2C2_Rcvbuf_String = (char * ) ptr + 1; } else error("Invalid variable"); I2C2_Rcvlen = rcvlen; I2C2_Sendlen = 0; char * buff = GetTempMemory(rcvlen > 255 ? rcvlen + 2 : STRINGSIZE); i2c2_masterCommand(1, (unsigned char * ) buff); } /************************************************************************************************** Enable the I2C1 module - master mode ***************************************************************************************************/ void i2c_enable(int bps) { ExtCfg(I2C0SDApin, EXT_COM_RESERVED, 0); ExtCfg(I2C0SCLpin, EXT_COM_RESERVED, 0); i2c_init(i2c0, bps * 1000); gpio_pull_up(PinDef[I2C0SDApin].GPno); gpio_pull_up(PinDef[I2C0SCLpin].GPno); I2C_enabled = 1; } void i2c2_enable(int bps) { ExtCfg(I2C1SDApin, EXT_COM_RESERVED, 0); ExtCfg(I2C1SCLpin, EXT_COM_RESERVED, 0); i2c_init(i2c1, bps * 1000); gpio_pull_up(PinDef[I2C1SDApin].GPno); gpio_pull_up(PinDef[I2C1SCLpin].GPno); I2C2_enabled = 1; } /************************************************************************************************** Disable the I2C1 module - master mode ***************************************************************************************************/ void i2c_disable() { if (I2C_Status & I2C_Status_Slave) { irq_set_enabled(I2C0_IRQ, false); irq_remove_handler(I2C0_IRQ, i2c0_irq_handler); i2c_set_slave_mode(i2c0, false, I2C_Slave_Addr); i2c0 -> hw -> intr_mask = 0; } I2C_Status = I2C_Status_Disable; I2C_Rcvbuf_String = NULL; // pointer to the master receive buffer I2C_Rcvbuf_Float = NULL; I2C_Rcvbuf_Int = NULL; I2C_Sendlen = 0; // length of the master send buffer I2C_Rcvlen = 0; // length of the master receive buffer I2C_Addr = 0; // I2C device address I2C_Timeout = 0; // master timeout value i2c_deinit(i2c0); I2C_enabled = 0; if (I2C0SDApin != 99) ExtCfg(I2C0SDApin, EXT_NOT_CONFIG, 0); if (I2C0SCLpin != 99) ExtCfg(I2C0SCLpin, EXT_NOT_CONFIG, 0); } void i2c2_disable() { if (I2C2_Status & I2C_Status_Slave) { irq_set_enabled(I2C1_IRQ, false); irq_remove_handler(I2C1_IRQ, i2c1_irq_handler); i2c_set_slave_mode(i2c1, false, I2C_Slave_Addr); i2c1 -> hw -> intr_mask = 0; } I2C2_Status = I2C_Status_Disable; I2C2_Rcvbuf_String = NULL; // pointer to the master receive buffer I2C2_Rcvbuf_Float = NULL; I2C2_Rcvbuf_Int = NULL; I2C2_Sendlen = 0; // length of the master send buffer I2C2_Rcvlen = 0; // length of the master receive buffer I2C2_Addr = 0; // I2C device address I2C2_Timeout = 0; // master timeout value i2c_deinit(i2c1); I2C2_enabled = 0; if (I2C1SDApin != 99) ExtCfg(I2C1SDApin, EXT_NOT_CONFIG, 0); if (I2C1SCLpin != 99) ExtCfg(I2C1SCLpin, EXT_NOT_CONFIG, 0); } /************************************************************************************************** Send and/or Receive data - master mode ***************************************************************************************************/ void i2c_masterCommand(int timer, unsigned char * I2C_Rcv_Buffer) { // unsigned char start_type, unsigned char i2caddr = I2C_Addr; if (I2C_Sendlen) { int i2cret = i2c_write_timeout_us(i2c0, (uint8_t) i2caddr, (uint8_t * ) I2C_Send_Buffer, I2C_Sendlen, (I2C_Status == I2C_Status_BusHold ? true : false), I2C_Timeout * 1000); mmI2Cvalue = 0; if (i2cret == PICO_ERROR_GENERIC) mmI2Cvalue = 1; if (i2cret == PICO_ERROR_TIMEOUT) mmI2Cvalue = 2; } if (I2C_Rcvlen) { int i2cret = i2c_read_timeout_us(i2c0, (uint8_t) i2caddr, (uint8_t * ) I2C_Rcv_Buffer, I2C_Rcvlen, (I2C_Status == I2C_Status_BusHold ? true : false), I2C_Timeout * 1000); mmI2Cvalue = 0; if (i2cret == PICO_ERROR_GENERIC) mmI2Cvalue = 1; if (i2cret == PICO_ERROR_TIMEOUT) mmI2Cvalue = 2; for (int i = 0; i < I2C_Rcvlen; i++) { if (I2C_Rcvbuf_String != NULL) { * I2C_Rcvbuf_String = I2C_Rcv_Buffer[i]; I2C_Rcvbuf_String++; } if (I2C_Rcvbuf_Float != NULL) { * I2C_Rcvbuf_Float = I2C_Rcv_Buffer[i]; I2C_Rcvbuf_Float++; } if (I2C_Rcvbuf_Int != NULL) { * I2C_Rcvbuf_Int = I2C_Rcv_Buffer[i]; I2C_Rcvbuf_Int++; } } } } void i2c2_masterCommand(int timer, unsigned char * I2C2_Rcv_Buffer) { // unsigned char start_type, unsigned char i2c2addr = I2C2_Addr; if (I2C2_Sendlen) { int i2cret = i2c_write_timeout_us(i2c1, (uint8_t) i2c2addr, (uint8_t * ) I2C_Send_Buffer, I2C2_Sendlen, (I2C2_Status == I2C_Status_BusHold ? true : false), I2C2_Timeout * 1000); mmI2Cvalue = 0; if (i2cret == PICO_ERROR_GENERIC) mmI2Cvalue = 1; if (i2cret == PICO_ERROR_TIMEOUT) mmI2Cvalue = 2; } if (I2C2_Rcvlen) { int i2cret = i2c_read_timeout_us(i2c1, (uint8_t) i2c2addr, (uint8_t * ) I2C2_Rcv_Buffer, I2C2_Rcvlen, (I2C2_Status == I2C_Status_BusHold ? true : false), I2C2_Timeout * 1000); mmI2Cvalue = 0; if (i2cret == PICO_ERROR_GENERIC) mmI2Cvalue = 1; if (i2cret == PICO_ERROR_TIMEOUT) mmI2Cvalue = 2; for (int i = 0; i < I2C2_Rcvlen; i++) { if (I2C2_Rcvbuf_String != NULL) { * I2C2_Rcvbuf_String = I2C2_Rcv_Buffer[i]; I2C2_Rcvbuf_String++; } if (I2C2_Rcvbuf_Float != NULL) { * I2C2_Rcvbuf_Float = I2C2_Rcv_Buffer[i]; I2C2_Rcvbuf_Float++; } if (I2C2_Rcvbuf_Int != NULL) { * I2C2_Rcvbuf_Int = I2C2_Rcv_Buffer[i]; I2C2_Rcvbuf_Int++; } } } } /* @endcond */ void fun_mmi2c(void) { iret = mmI2Cvalue; targ = T_INT; } /* * @cond * The following section will be excluded from the documentation. */ void GeneralSend(unsigned int addr, int nbr, char * p) { if (I2C0locked) { I2C_Sendlen = nbr; // send one byte I2C_Rcvlen = 0; memcpy(I2C_Send_Buffer, p, nbr); I2C_Addr = addr; // address of the device i2c_masterCommand(1, NULL); } else { I2C2_Sendlen = nbr; // send one byte I2C2_Rcvlen = 0; memcpy(I2C_Send_Buffer, p, nbr); I2C2_Addr = addr; // address of the device i2c2_masterCommand(1, NULL); } } void GeneralReceive(unsigned int addr, int nbr, char * p) { if (I2C0locked) { I2C_Rcvbuf_Float = NULL; I2C_Rcvbuf_Int = NULL; I2C_Sendlen = 0; // send one byte I2C_Rcvlen = nbr; I2C_Addr = addr; // address of the device i2c_masterCommand(1, (unsigned char * ) p); } else { I2C2_Rcvbuf_Float = NULL; I2C2_Rcvbuf_Int = NULL; I2C2_Sendlen = 0; // send one byte I2C2_Rcvlen = nbr; I2C2_Addr = addr; // address of the device i2c2_masterCommand(1, (unsigned char * ) p); } } void WiiSend(int nbr, char * p) { unsigned int addr = nunaddr; GeneralSend(addr, nbr, p); } void WiiReceive(int nbr, char * p) { unsigned int addr = nunaddr; GeneralReceive(addr, nbr, p); } uint8_t readRegister8(unsigned int addr, uint8_t reg) { uint8_t buff; GeneralSend(addr, 1, (char * ) & reg); GeneralReceive(addr, 1, (char * ) & buff); return buff; } uint32_t readRegister32(unsigned int addr, uint8_t reg) { uint32_t buff; GeneralSend(addr, 1, (char * ) & reg); GeneralReceive(addr, 4, (char * ) & buff); return buff; } void WriteRegister8(unsigned int addr, uint8_t reg, uint8_t data) { uint8_t buff[2]; buff[0] = reg; buff[1] = data; GeneralSend(addr, 2, (char * ) buff); } void Write8Register16(unsigned int addr, uint16_t reg, uint8_t data) { uint8_t buff[3]; buff[0] = reg >> 8; buff[1] = reg & 0xFF; buff[2] = data; GeneralSend(addr, 3, (char * ) buff); } uint8_t read8Register16(unsigned int addr, uint16_t reg) { uint8_t buff; uint8_t rbuff[2]; rbuff[0] = reg >> 8; rbuff[1] = reg & 0xFF; if (I2C0locked) I2C_Status = I2C_Status_BusHold; else I2C2_Status = I2C_Status_BusHold; GeneralSend(addr, 2, (char * ) rbuff); if (I2C0locked) I2C_Status = 0; else I2C2_Status = 0; GeneralReceive(addr, 1, (char * ) & buff); return buff; } void nunproc(void) { static int lastc = 0, lastz = 0; nunstruct[5].x = nunbuff[0]; nunstruct[5].y = nunbuff[1]; nunstruct[5].ax = nunbuff[2] << 2; nunstruct[5].ay = nunbuff[3] << 2; nunstruct[5].az = nunbuff[4] << 2; nunstruct[5].Z = (~(nunbuff[5] & 1)) & 1; nunstruct[5].C = (~((nunbuff[5] & 2) >> 1)) & 1; nunstruct[5].ax += ((nunbuff[5] >> 2) & 3); nunstruct[5].ay += ((nunbuff[5] >> 4) & 3); nunstruct[5].az += ((nunbuff[5] >> 6) & 3); if (lastc == 0 && nunstruct[5].C) { lastc = 1; nunfoundc[5] = 1; } if (lastz == 0 && nunstruct[5].Z) { lastz = 1; nunfoundc[5] = 1; } if (nunstruct[5].C == 0) lastc = 0; if (nunstruct[5].Z == 0) lastz = 0; } /* @endcond */ void MIPS16 cmd_Nunchuck(void) { unsigned char * tp = NULL; uint32_t id = 0; if ((tp = checkstring(cmdline, (unsigned char * ) "OPEN"))) { getargs( & tp, 1, (unsigned char * ) ","); if (!(I2C0locked || I2C1locked)) error("SYSTEM I2C not configured"); if (classic1 || nunchuck1) error("Already open"); memset((void * ) & nunstruct[5].x, 0, sizeof(nunstruct[5])); int retry = 5; do { WiiSend(sizeof(nuninit), (char * ) nuninit); uSec(5000); } while (mmI2Cvalue && retry--); if (mmI2Cvalue) error("Nunchuck not connected"); WiiSend(sizeof(nuninit2), (char * ) nuninit2); if (mmI2Cvalue) error("Nunchuck not connected"); uSec(5000); retry = 5; do { WiiSend(sizeof(nunid), (char * ) nunid); uSec(5000); WiiReceive(4, (char * ) & id); uSec(5000); } while (mmI2Cvalue && retry--); if (mmI2Cvalue) error("Device ID not returned"); nunstruct[5].type = swap32(id); if (nunstruct[5].type != 0xA4200000) error("Device connected is not a Nunchuck"); uSec(5000); retry = 5; nunbuff[5] = 0; if (argc == 1) { nunInterruptc[5] = (char * ) GetIntAddress(argv[0]); // get the interrupt location InterruptUsed = true; } nunchuck1 = 1; while (nunchuck1 == 1) routinechecks(); if (nunbuff[5] == 0 || nunbuff[5] == 255) { nunchuck1 = 0; error("Nunchuck not responding"); } nunproc(); return; } else if ((tp = checkstring(cmdline, (unsigned char * ) "CLOSE"))) { if (!nunchuck1) error("Not open"); nunchuck1 = 0; nunchuckread = false; WiiReceive(6, (char * ) nunbuff); nunInterruptc[5] = NULL; } else error("Syntax"); } void MIPS16 cmd_Classic(void) { unsigned char * tp = NULL; uint32_t id = 0; if ((tp = checkstring(cmdline, (unsigned char * ) "OPEN"))) { getargs( & tp, 3, (unsigned char * ) ","); if (!(I2C0locked || I2C1locked)) error("SYSTEM I2C not configured"); if (classic1 || nunchuck1) error("Already open"); memset((void * ) & nunstruct[0].x, 0, sizeof(nunstruct[0])); int retry = 5; do { WiiSend(sizeof(nuninit), (char * ) nuninit); uSec(5000); } while (mmI2Cvalue && retry--); if (mmI2Cvalue) error("Classic not connected"); WiiSend(sizeof(nuninit2), (char * ) nuninit2); if (mmI2Cvalue) error("Classic not connected"); uSec(5000); retry = 5; do { WiiSend(sizeof(nunid), (char * ) nunid); uSec(5000); WiiReceive(4, (char * ) & id); uSec(5000); } while (mmI2Cvalue && retry--); if (mmI2Cvalue) error("Device ID not returned"); nunstruct[0].type = swap32(id); if (nunstruct[0].type == 0xA4200000) error("Device connected is a Nunchuck"); uSec(5000); if (argc >= 1) { nunInterruptc[0] = (char * ) GetIntAddress(argv[0]); // get the interrupt location InterruptUsed = true; nunstruct[0].x1 = 0b111111111111111; if (argc == 3) nunstruct[0].x1 = getint(argv[2], 0, 0b111111111111111); } classic1 = 1; while (classic1 == 1) routinechecks(); if (nunbuff[0] == 0 || nunbuff[0] == 255) { classic1 = 0; error("Classic not responding"); } classicproc(); return; } else if ((tp = checkstring(cmdline, (unsigned char * ) "CLOSE"))) { if (!classic1) error("Not open"); classic1 = 0; classicread = false; WiiReceive(6, (char * ) nunbuff); nunInterruptc[0] = NULL; } else error("Syntax"); } /* * @cond * The following section will be excluded from the documentation. */ void classicproc(void) { // int ax; //classic left x // int ay; //classic left y // int az; //classic centre // int Z; //classic right x // int C; //classic right y // int L; //classic left analog // int R; //classic right analog // unsigned short x0; //classic buttons static unsigned short buttonlast = 0; unsigned short inttest = (((nunbuff[4] >> 1) | (nunbuff[5] << 7)) ^ 0b111111111111111) & nunstruct[0].x1; nunstruct[0].classic[0] = nunbuff[0]; nunstruct[0].classic[1] = nunbuff[1]; nunstruct[0].classic[2] = nunbuff[2]; nunstruct[0].classic[3] = nunbuff[3]; nunstruct[0].classic[4] = nunbuff[4]; nunstruct[0].classic[5] = nunbuff[5]; if (inttest != buttonlast) { nunfoundc[0] = 1; } buttonlast = inttest; nunstruct[0].ax = (nunbuff[0] & 0b111111) << 2; nunstruct[0].ay = (nunbuff[1] & 0b111111) << 2; nunstruct[0].Z = (((nunbuff[2] & 0b10000000) >> 7) | ((nunbuff[1] & 0b11000000) >> 5) | ((nunbuff[0] & 0b11000000) >> 3)) << 3; nunstruct[0].C = (nunbuff[2] & 0b11111) << 3; nunstruct[0].R = ((nunbuff[3] & 0b00011111)) << 3; nunstruct[0].L = (((nunbuff[3] & 0b11100000) >> 5) | ((nunbuff[2] & 0b01100000) >> 2)) << 3; nunstruct[0].x0 = ((nunbuff[4] >> 1) | (nunbuff[5] << 7)) ^ 0b111111111111111; } #ifndef PICOMITEVGA #define ov7670_address 0x21 #define top 120 #define left 160 uint8_t PCLK = 0; uint8_t XCLK = 0; uint8_t HREF = 0; uint8_t VSYNC = 0; uint8_t RESET = 0; uint8_t D0 = 0; uint8_t XCLKGP = 0; uint8_t PCLKGP = 0; uint8_t VSYNCGP = 0; uint8_t HREFGP = 0; //extern volatile int ExtCurrentConfig[]; void cameraclose(void) { if (PCLK) ExtCfg(PCLK, EXT_NOT_CONFIG, 0); if (HREF) ExtCfg(HREF, EXT_NOT_CONFIG, 0); if (VSYNC) ExtCfg(VSYNC, EXT_NOT_CONFIG, 0); if (RESET) ExtCfg(RESET, EXT_NOT_CONFIG, 0); if (D0) { int startdata = PinDef[D0].GPno; for (int i = startdata; i < startdata + 8; i++) { ExtCfg(PINMAP[i], EXT_NOT_CONFIG, 0); } } if (XCLK) { PWMoff(CameraSlice); ExtCfg(XCLK, EXT_NOT_CONFIG, 0); } CameraSlice = -1; CameraChannel = -1; PCLK = HREF = VSYNC = D0 = XCLK = 0; } int readregister(int reg) { unsigned char buff[2]; if (I2C0locked) { I2C_Sendlen = 1; // send one byte I2C_Rcvlen = 0; * I2C_Send_Buffer = reg; // the first register to read I2C_Addr = ov7670_address; // address of the device i2c_masterCommand(1, NULL); } else { I2C2_Sendlen = 1; // send one byte I2C2_Rcvlen = 0; * I2C_Send_Buffer = reg; // the first register to read I2C2_Addr = ov7670_address; // address of the device i2c2_masterCommand(1, NULL); } if (mmI2Cvalue) { cameraclose(); error("I2C failure"); } uSec(1000); if (I2C0locked) { I2C_Rcvbuf_Float = NULL; I2C_Rcvbuf_Int = NULL; I2C_Rcvlen = 1; // get 7 bytes I2C_Sendlen = 0; I2C_Addr = ov7670_address; // address of the device i2c_masterCommand(1, buff); } else { I2C2_Rcvbuf_Float = NULL; I2C2_Rcvbuf_Int = NULL; I2C2_Rcvlen = 1; // get 7 bytes I2C2_Sendlen = 0; I2C2_Addr = ov7670_address; // address of the device i2c2_masterCommand(1, buff); } uSec(1000); return buff[0]; } void ov7670_set(char a, char b) { //send the command if (I2C0locked) { I2C_Sendlen = 2; // send one byte I2C_Rcvlen = 0; I2C_Send_Buffer[0] = a; // the first register to read I2C_Send_Buffer[1] = b; // the first register to read I2C_Addr = ov7670_address; // address of the device i2c_masterCommand(1, NULL); } else { I2C2_Sendlen = 2; // send one byte I2C2_Rcvlen = 0; I2C_Send_Buffer[0] = a; // the first register to read I2C_Send_Buffer[1] = b; // the first register to read I2C2_Addr = ov7670_address; // address of the device i2c2_masterCommand(1, NULL); } if (mmI2Cvalue) { cameraclose(); error("I2C failure"); } if (a == REG_COM7 && b == COM7_RESET) { uSec(500000); return; } uSec(1000); if (readregister(a) != b) error("Camera Config Failure"); uSec(1000); return; } void OV7670_test_pattern(OV7670_pattern pattern) { // Read current SCALING_XSC and SCALING_YSC register settings, // so image scaling settings aren't corrupted. uint8_t xsc = readregister(OV7670_REG_SCALING_XSC); uint8_t ysc = readregister(OV7670_REG_SCALING_YSC); if (pattern & 1) { xsc |= 0x80; } else { xsc &= ~0x80; } if (pattern & 2) { ysc |= 0x80; } else { ysc &= ~0x80; } // Write modified results back to SCALING_XSC and SCALING_YSC registers ov7670_set(OV7670_REG_SCALING_XSC, xsc); ov7670_set(OV7670_REG_SCALING_YSC, ysc); } static const OV7670_command OV7670_yuv[] = { // Manual output format, YUV, use full output range { OV7670_REG_COM7, OV7670_COM7_YUV }, { OV7670_REG_COM15, OV7670_COM15_R00FF }, { 0xFF, 0xFF } }, OV7670_rgb[] = { // Manual output format, RGB, use RGB565 and full 0-255 output range { OV7670_REG_COM7, OV7670_COM7_RGB }, { OV7670_REG_RGB444, 0 }, { OV7670_REG_COM15, OV7670_COM15_RGB565 | OV7670_COM15_R00FF }, { 0xFF, 0xFF } }; void OV7670_frame_control(uint8_t size, uint8_t vstart, uint16_t hstart, uint8_t edge_offset, uint8_t pclk_delay) { uint8_t value; // Enable downsampling if sub-VGA, and zoom if 1:16 scale value = (size > OV7670_SIZE_DIV1) ? OV7670_COM3_DCWEN : 0; if (size == OV7670_SIZE_DIV16) value |= OV7670_COM3_SCALEEN; ov7670_set(OV7670_REG_COM3, value); // Enable PCLK division if sub-VGA 2,4,8,16 = 0x19,1A,1B,1C value = (size > OV7670_SIZE_DIV1) ? (0x18 + size) : 0; ov7670_set(OV7670_REG_COM14, value); // Horiz/vert downsample ratio, 1:8 max (H,V are always equal for now) value = (size <= OV7670_SIZE_DIV8) ? size : OV7670_SIZE_DIV8; ov7670_set(OV7670_REG_SCALING_DCWCTR, value * 0x11); // Pixel clock divider if sub-VGA value = (size > OV7670_SIZE_DIV1) ? (0xF0 + size) : 0x08; ov7670_set(OV7670_REG_SCALING_PCLK_DIV, value); // Apply 0.5 digital zoom at 1:16 size (others are downsample only) value = (size == OV7670_SIZE_DIV16) ? 0x40 : 0x20; // 0.5, 1.0 // Read current SCALING_XSC and SCALING_YSC register values because // test pattern settings are also stored in those registers and we // don't want to corrupt anything there. uint8_t xsc = readregister(OV7670_REG_SCALING_XSC); uint8_t ysc = readregister(OV7670_REG_SCALING_YSC); xsc = (xsc & 0x80) | value; // Modify only scaling bits (not test pattern) ysc = (ysc & 0x80) | value; // Write modified result back to SCALING_XSC and SCALING_YSC ov7670_set(OV7670_REG_SCALING_XSC, xsc); ov7670_set(OV7670_REG_SCALING_YSC, ysc); // Window size is scattered across multiple registers. // Horiz/vert stops can be automatically calc'd from starts. uint16_t vstop = vstart + 480; uint16_t hstop = (hstart + 640) % 784; ov7670_set(OV7670_REG_HSTART, hstart >> 3); ov7670_set(OV7670_REG_HSTOP, hstop >> 3); ov7670_set(OV7670_REG_HREF, (edge_offset << 6) | ((hstop & 0b111) << 3) | (hstart & 0b111)); ov7670_set(OV7670_REG_VSTART, vstart >> 2); ov7670_set(OV7670_REG_VSTOP, vstop >> 2); ov7670_set(OV7670_REG_VREF, ((vstop & 0b11) << 2) | (vstart & 0b11)); ov7670_set(OV7670_REG_SCALING_PCLK_DELAY, pclk_delay); } void OV7670_set_size(OV7670_size size) { // Array of five window settings, index of each (0-4) aligns with the five // OV7670_size enumeration values. If enum changes, list must change! static struct { uint8_t vstart; uint8_t hstart; uint8_t edge_offset; uint8_t pclk_delay; } window[] = { // Window settings were tediously determined empirically. // I hope there's a formula for this, if a do-over is needed. { 9, 162, 2, 2 }, // SIZE_DIV1 640x480 VGA { 10, 174, 4, 2 }, // SIZE_DIV2 320x240 QVGA { 11, 186, 2, 2 }, // SIZE_DIV4 160x120 QQVGA { 12, 210, 0, 2 }, // SIZE_DIV8 80x60 ... { 15, 252, 3, 2 }, // SIZE_DIV16 40x30 }; OV7670_frame_control(size, window[size].vstart, window[size].hstart, window[size].edge_offset, window[size].pclk_delay); } #define ST_PCLK gpio_get(PCLKGP) #define ST_HREF gpio_get(HREFGP) #define ST_VSYNC gpio_get(VSYNCGP) void __not_in_flash_func(capture)(char * buff) { char * k = buff; while (ST_VSYNC) {} /* wait for the old frame to end */ while (!ST_VSYNC) {} /* wait for a new frame to start */ //At this point VSync has gone high and the frame is about to start while (!ST_HREF) {} // wait for the first line to start while (!ST_PCLK) {} // wait for clock to go high / while (ST_PCLK) {} // wait for clock to go back low / for (int i = 0; i < 160; i++) { while (!ST_PCLK) {} // wait for clock to go high / * k++ = gpio_get_all64() >> PinDef[D0].GPno; while (ST_PCLK) {} // wait for clock to go back low / // second byte/ while (!ST_PCLK) {} // wait for clock to go high / * k++ = gpio_get_all64() >> PinDef[D0].GPno; while (ST_PCLK) {} // wait for clock to go back low / } while (ST_HREF) {} // wait for the first line to end*/ k = buff; for (int j = 0; j < 119; j++) { while (!ST_HREF) {} // wait for the first line to end for (int i = 0; i < 160; i++) { while (!ST_PCLK) {} // wait for clock to go high / * k++ = gpio_get_all64() >> PinDef[D0].GPno; while (ST_PCLK) {} // wait for clock to go back low / // second byte/ while (!ST_PCLK) {} // wait for clock to go high / * k++ = gpio_get_all64() >> PinDef[D0].GPno; while (ST_PCLK) {} // wait for clock to go back low / } while (ST_HREF) {} // wait for the first line to end } } void saturation(int s) //-2 to 2 { //color matrix values ov7670_set(0x4f, 0x80 + 0x20 * s); ov7670_set(0x50, 0x80 + 0x20 * s); ov7670_set(0x51, 0x00); ov7670_set(0x52, 0x22 + (0x11 * s) / 2); ov7670_set(0x53, 0x5e + (0x2f * s) / 2); ov7670_set(0x54, 0x80 + 0x20 * s); ov7670_set(0x58, 0x9e); //matrix signs } /* @endcond */ void MIPS16 cmd_camera(void) { union colourmap { char rgbbytes[4]; unsigned int rgb; } c; unsigned char * tp = NULL; if ((tp = checkstring(cmdline, (unsigned char * ) "OPEN"))) { int pin1, pin2, pin3, pin4, pin5, pin6; getargs( & tp, 11, (unsigned char * ) ","); if (argc != 11) error("Syntax"); if (!(Option.DISPLAY_TYPE > I2C_PANEL && Option.DISPLAY_TYPE < BufferedPanel)) error("Invalid display type"); if (!(I2C0locked || I2C1locked)) error("SYSTEM I2C not configured"); if (XCLK) error("Camera already open"); unsigned char code; //XCLK pin if (!(code = codecheck(argv[0]))) argv[0] += 2; pin1 = getinteger(argv[0]); if (!code) pin1 = codemap(pin1); if (IsInvalidPin(pin1)) error("Invalid pin"); if (ExtCurrentConfig[pin1] != EXT_NOT_CONFIG) error("Pin %/| is in use", pin1, pin1); int slice = getslice(pin1); if ((PinDef[Option.DISPLAY_BL].slice & 0x7f) == slice) error("Channel in use for backlight"); if ((PinDef[pin1].slice & 0x7f) == Option.AUDIO_SLICE) error("Channel in use for Audio"); //PCLK pin if (!(code = codecheck(argv[2]))) argv[2] += 2; pin2 = getinteger(argv[2]); if (!code) pin2 = codemap(pin2); if (IsInvalidPin(pin2)) error("Invalid pin"); if (ExtCurrentConfig[pin2] != EXT_NOT_CONFIG) error("Pin %/| is in use", pin2, pin2); //HREF pin if (!(code = codecheck(argv[4]))) argv[4] += 2; pin3 = getinteger(argv[4]); if (!code) pin3 = codemap(pin3); if (IsInvalidPin(pin3)) error("Invalid pin"); if (ExtCurrentConfig[pin3] != EXT_NOT_CONFIG) error("Pin %/| is in use", pin3, pin3); //VSYNC pin if (!(code = codecheck(argv[6]))) argv[6] += 2; pin4 = getinteger(argv[6]); if (!code) pin4 = codemap(pin4); if (IsInvalidPin(pin4)) error("Invalid pin"); if (ExtCurrentConfig[pin4] != EXT_NOT_CONFIG) error("Pin %/| is in use", pin4, pin4); //RESET pin if (!(code = codecheck(argv[8]))) argv[8] += 2; pin5 = getinteger(argv[8]); if (!code) pin5 = codemap(pin5); if (IsInvalidPin(pin5)) error("Invalid pin"); if (ExtCurrentConfig[pin5] != EXT_NOT_CONFIG) error("Pin %/| is in use", pin5, pin5); //D0-D7 pins if (!(code = codecheck(argv[10]))) argv[10] += 2; pin6 = getinteger(argv[10]); if (!code) pin6 = codemap(pin6); if (IsInvalidPin(pin6)) error("Invalid pin"); int startdata = PinDef[pin6].GPno; for (int i = startdata; i < startdata + 8; i++) { if (IsInvalidPin(PINMAP[i])) error("Invalid pin"); if (ExtCurrentConfig[PINMAP[i]] != EXT_NOT_CONFIG) error("Pin %/| is in use", PINMAP[i], PINMAP[i]); } XCLK = pin1; PCLK = pin2; HREF = pin3; VSYNC = pin4; RESET = pin5; D0 = pin6; setpwm(pin1, & CameraChannel, & CameraSlice, 12000000.0, 50.0); ExtCfg(XCLK, EXT_COM_RESERVED, 0); ExtCfg(PCLK, EXT_DIG_IN, 0); ExtCfg(PCLK, EXT_COM_RESERVED, 0); ExtCfg(HREF, EXT_DIG_IN, 0); ExtCfg(HREF, EXT_COM_RESERVED, 0); ExtCfg(VSYNC, EXT_DIG_IN, 0); ExtCfg(VSYNC, EXT_COM_RESERVED, 0); ExtCfg(RESET, EXT_DIG_OUT, 1); ExtCfg(RESET, EXT_COM_RESERVED, 0); for (int i = startdata; i < startdata + 8; i++) { ExtCfg(PINMAP[i], EXT_DIG_IN, 0); ExtCfg(PINMAP[i], EXT_COM_RESERVED, 0); } PCLKGP = PinDef[PCLK].GPno; VSYNCGP = PinDef[VSYNC].GPno; HREFGP = PinDef[HREF].GPno; uSec(1000); PinSetBit(pin5, LATCLR); uSec(1000); PinSetBit(pin5, LATSET); uSec(1000); if (readregister(REG_PID) != 118) error("Camera not found"); ov7670_set(REG_COM7, COM7_RESET); // RESET CAMERA ov7670_set(REG_COM7, COM7_RESET); // RESET CAMERA ov7670_set(REG_RGB444, 0); ov7670_set(REG_COM10, 0x02); // 0x02 VSYNC negative (http://nasulica.homelinux.org/?p=959) ov7670_set(REG_MVFP, 0x37); // ov7670_set( REG_CLKRC, 0x40); ov7670_set(REG_COM11, 0x0A); ov7670_set(REG_COM7, COM7_RGB); ov7670_set(REG_COM1, 0); ov7670_set(REG_COM15, COM15_RGB565); ov7670_set(REG_COM9, 0x2A); ov7670_set(REG_TSLB, 0x04); // 0D = UYVY 04 = YUYV ov7670_set(REG_COM13, 0x88); ov7670_set(REG_HSTART, 0x13); ov7670_set(REG_HSTOP, 0x01); ov7670_set(REG_HREF, 0xb6); ov7670_set(REG_VSTART, 0x02); ov7670_set(REG_VSTOP, 0x7a); ov7670_set(REG_VREF, 0x0a); ov7670_set(REG_COM5, 0x61); ov7670_set(REG_COM6, 0x4b); ov7670_set(0x16, 0x02); ov7670_set(0x21, 0x02); ov7670_set(0x22, 0x91); ov7670_set(0x29, 0x07); ov7670_set(0x33, 0x0b); ov7670_set(0x35, 0x0b); ov7670_set(0x37, 0x1d); ov7670_set(0x38, 0x71); ov7670_set(0x39, 0x2a); ov7670_set(REG_COM12, 0x78); ov7670_set(0x4d, 0x40); ov7670_set(0x4e, 0x20); ov7670_set(REG_GFIX, 0); ov7670_set(0x74, 0x10); ov7670_set(0x8d, 0x4f); ov7670_set(0x8e, 0); ov7670_set(0x8f, 0); ov7670_set(0x90, 0); ov7670_set(0x91, 0); ov7670_set(0x96, 0); ov7670_set(0x9a, 0); ov7670_set(0xb0, 0x84); ov7670_set(0xb1, 0x0c); ov7670_set(0xb2, 0x0e); ov7670_set(0xb3, 0x82); // ov7670_set(0xb8, 0x0a); ov7670_set(0x7a, 0x20); //gamma correction ov7670_set(0x7b, 0x10); ov7670_set(0x7c, 0x1e); ov7670_set(0x7d, 0x35); ov7670_set(0x7e, 0x5a); ov7670_set(0x7f, 0x69); ov7670_set(0x80, 0x76); ov7670_set(0x81, 0x80); ov7670_set(0x82, 0x88); ov7670_set(0x83, 0x8f); ov7670_set(0x84, 0x96); ov7670_set(0x85, 0xa3); ov7670_set(0x86, 0xaf); ov7670_set(0x87, 0xc4); ov7670_set(0x88, 0xd7); ov7670_set(0x89, 0xe8); // AGC and AEC parameters. Note we start by disabling those features, //then turn them only after tweaking the values. ov7670_set(0x13, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT); ov7670_set(0x00, 0); ov7670_set(0x10, 0); ov7670_set(0x0d, 0x40); ov7670_set(0x14, 0x18); ov7670_set(0xa5, 0x05); ov7670_set(0xab, 0x07); ov7670_set(0x24, 0x95); ov7670_set(0x25, 0x33); ov7670_set(0x26, 0xe3); ov7670_set(0x9f, 0x78); ov7670_set(0xa0, 0x68); ov7670_set(0xa1, 0x03); ov7670_set(0xa6, 0xd8); ov7670_set(0xa7, 0xd8); ov7670_set(0xa8, 0xf0); ov7670_set(0xa9, 0x90); ov7670_set(0xaa, 0x94); ov7670_set(0x13, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT | COM8_AGC | COM8_AEC); // Almost all of these are magic "reserved" values. */ ov7670_set(0x0e, 0x61); ov7670_set(0x0f, 0x4b); ov7670_set(0x16, 0x02); // ov7670_set(0x1e, 0x27); ov7670_set(0x21, 0x02); ov7670_set(0x22, 0x91); ov7670_set(0x29, 0x07); ov7670_set(0x33, 0x0b); ov7670_set(0x35, 0x0b); ov7670_set(0x37, 0x1d); ov7670_set(0x38, 0x71); ov7670_set(0x39, 0x2a); // ov7670_set(0x3c, 0x78); ov7670_set(0x4d, 0x40); ov7670_set(0x4e, 0x20); ov7670_set(0x69, 0); // ov7670_set(0x6b, 0x0a); ov7670_set(0x74, 0x10); ov7670_set(0x8d, 0x4f); ov7670_set(0x8e, 0); ov7670_set(0x8f, 0); ov7670_set(0x90, 0); ov7670_set(0x91, 0); ov7670_set(0x96, 0); ov7670_set(0x9a, 0); ov7670_set(0xb0, 0x84); ov7670_set(0xb1, 0x0c); ov7670_set(0xb2, 0x0e); ov7670_set(0xb3, 0x82); ov7670_set(0xb8, 0x0a); // More reserved magic, some of which tweaks white balance */ ov7670_set(0x43, 0x0a); ov7670_set(0x44, 0xf0); ov7670_set(0x45, 0x34); ov7670_set(0x46, 0x58); ov7670_set(0x47, 0x28); ov7670_set(0x48, 0x3a); ov7670_set(0x59, 0x88); ov7670_set(0x5a, 0x88); ov7670_set(0x5b, 0x44); ov7670_set(0x5c, 0x67); ov7670_set(0x5d, 0x49); ov7670_set(0x5e, 0x0e); ov7670_set(0x6c, 0x0a); ov7670_set(0x6d, 0x55); ov7670_set(0x6e, 0x11); ov7670_set(0x6f, 0x9f); ov7670_set(0x6a, 0x40); ov7670_set(0x01, 0x40); ov7670_set(0x02, 0x60); // COLOR SETTING ov7670_set(0x4f, 0x80); ov7670_set(0x50, 0x80); ov7670_set(0x51, 0x00); ov7670_set(0x52, 0x22); ov7670_set(0x53, 0x5e); ov7670_set(0x54, 0x80); ov7670_set(0x56, 0x40); ov7670_set(0x58, 0x9e); ov7670_set(0x59, 0x88); ov7670_set(0x5a, 0x88); ov7670_set(0x5b, 0x44); ov7670_set(0x5c, 0x67); ov7670_set(0x5d, 0x49); ov7670_set(0x5e, 0x0e); ov7670_set(0x69, 0x00); ov7670_set(0x6a, 0x40); ov7670_set(0x6b, 0x0a); ov7670_set(0x6c, 0x0a); ov7670_set(0x6d, 0x55); ov7670_set(0x6e, 0x11); ov7670_set(0x6f, 0x9f); ov7670_set(0xb0, 0x84); ov7670_set(0x13, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT | COM8_AGC | COM8_AEC | COM8_AWB); // Matrix coefficients */ ov7670_set(0x4f, 0x80); ov7670_set(0x50, 0x80); ov7670_set(0x51, 0); ov7670_set(0x52, 0x22); ov7670_set(0x53, 0x5e); ov7670_set(0x54, 0x80); ov7670_set(0x58, 0x9e); ov7670_set(0x41, 0x08); ov7670_set(0x3f, 0); ov7670_set(0x75, 0x05); ov7670_set(0x76, 0xe1); ov7670_set(0x4c, 0); ov7670_set(0x77, 0x01); ov7670_set(0x3d, 0xc3); ov7670_set(0x4b, 0x09); ov7670_set(0x41, 0x38); ov7670_set(0x56, 0x40); ov7670_set(0x34, 0x11); ov7670_set(0x3b, COM11_EXP | COM11_HZAUTO); ov7670_set(0xa4, 0x88); ov7670_set(0x96, 0); ov7670_set(0x97, 0x30); ov7670_set(0x98, 0x20); ov7670_set(0x99, 0x30); ov7670_set(0x9a, 0x84); ov7670_set(0x9b, 0x29); ov7670_set(0x9c, 0x03); ov7670_set(0x9d, 0x4c); ov7670_set(0x9e, 0x3f); ov7670_set(0x78, 0x04); // Extra-weird stuff. Some sort of multiplexor register */ ov7670_set(0x79, 0x01); ov7670_set(0xc8, 0xf0); ov7670_set(0x79, 0x0f); ov7670_set(0xc8, 0x00); ov7670_set(0x79, 0x10); ov7670_set(0xc8, 0x7e); ov7670_set(0x79, 0x0a); ov7670_set(0xc8, 0x80); ov7670_set(0x79, 0x0b); ov7670_set(0xc8, 0x01); ov7670_set(0x79, 0x0c); ov7670_set(0xc8, 0x0f); ov7670_set(0x79, 0x0d); ov7670_set(0xc8, 0x20); ov7670_set(0x79, 0x09); ov7670_set(0xc8, 0x80); ov7670_set(0x79, 0x02); ov7670_set(0xc8, 0xc0); ov7670_set(0x79, 0x03); ov7670_set(0xc8, 0x40); ov7670_set(0x79, 0x05); ov7670_set(0xc8, 0x30); ov7670_set(0x79, 0x26); // for (int i = 0; OV7670_init[i].reg <= OV7670_REG_LAST; i++) { // ov7670_set(OV7670_init[i].reg, OV7670_init[i].value); // } saturation(1); OV7670_set_size(OV7670_SIZE_DIV4); ov7670_set(REG_COM10, 0x02); // 0x02 VSYNC negative (http://nasulica.homelinux.org/?p=959) //check the input signals uint64_t us = time_us_64() + 1000000; while (!ST_PCLK && time_us_64() < us) {} /* wait for clock to go high */ while (ST_PCLK && time_us_64() < us) {} /* wait for clock to go back low */ if (time_us_64() > us) error("Timeout on camera PCLK signal"); while (ST_HREF && time_us_64() < us) {} /* wait for a line to end */ while (!ST_HREF && time_us_64() < us) {} /* wait for a line to end */ if (time_us_64() > us) error("Timeout on camera HREF signal"); while (ST_VSYNC && time_us_64() < us) {} /* wait for the old frame to end */ while (!ST_VSYNC && time_us_64() < us) {} /* wait for a new frame to start */ if (time_us_64() > us) error("Timeout on camera VSYNC signal"); // OV7670_test_pattern(OV7670_TEST_PATTERN_COLOR_BAR); } else if ((tp = checkstring(cmdline, (unsigned char * ) "TEST"))) { getargs( & tp, 1, (unsigned char * ) ","); OV7670_test_pattern(getint(argv[0], 0, 3)); } else if ((tp = checkstring(cmdline, (unsigned char * ) "REGISTER"))) { getargs( & tp, 3, (unsigned char * ) ","); if (!XCLK) error("Camera not open"); int a = getint(argv[0], 0, 255); int b = getint(argv[2], 0, 255); int c = readregister(a); ov7670_set(a, b); MMPrintString("Register &H"); PIntH(a); MMPrintString(" was &H"); PIntH(c); MMPrintString(" now &H"); PIntH(b); PRet(); } else if ((tp = checkstring(cmdline, (unsigned char * ) "CHANGE"))) { int size = 0; unsigned char * cp = NULL; int totaldifference = 0, difference; if (!XCLK) error("Camera not open"); getargs( & tp, 9, (unsigned char * ) ","); if (!(argc == 3 || argc == 5 || argc == 9)) error("Syntax"); int xs = 0, ys = 0; if (!XCLK) error("Camera not open"); int scale = 1; int64_t * aint; size = parseintegerarray(argv[0], & aint, 1, 1, NULL, true); cp = (unsigned char * ) aint; // get the two variables MMFLOAT * outdiff = findvar(argv[2], V_FIND); if (!(g_vartbl[g_VarIndex].type & T_NBR)) error("Invalid variable"); if (size < 160 * 120 / 8) error("Array too small"); int picout = 0; if (argc >= 5) { scale = getint(argv[4], 1, HRes / 160); picout = 1; if (argc == 9) { xs = getint(argv[6], 0, HRes - 1); ys = getint(argv[8], 0, VRes - 1); } } for (int i = 0; OV7670_yuv[i].reg <= OV7670_REG_LAST; i++) { ov7670_set(OV7670_yuv[i].reg, OV7670_yuv[i].value); } char * buff = GetTempMemory(160 * 120 * 2); char * k = buff; c.rgb = 0; disable_interrupts_pico(); capture(buff); enable_interrupts_pico(); char * linebuff = NULL; if (scale) linebuff = GetTempMemory(160 * 3); for (int y = ys; y < 120 * scale + ys; y += scale) { int kk = 0; for (int x = 0; x < 160; x++) { c.rgbbytes[1] = * k++; c.rgbbytes[0] = * k++; if (c.rgbbytes[1] > * cp) difference = (c.rgbbytes[1] - * cp); else difference = ( * cp - c.rgbbytes[1]); totaldifference += difference; * cp++ = c.rgbbytes[1]; if (picout) { for (int r = 0; r < scale; r++) { linebuff[kk++] = difference; linebuff[kk++] = difference; linebuff[kk++] = difference; } } } if (picout) { for (int r = 0; r < scale; r++) { if (y + r < VRes) { int w = 160 * scale; if (w > HRes - xs) w = HRes - xs; DrawBuffer(xs, y + r, xs + w - 1, y + r, (unsigned char * ) linebuff); } } } } * outdiff = (MMFLOAT) totaldifference / (160.0 * 120.0 * 255.0) * 100.0; } else if ((tp = checkstring(cmdline, (unsigned char * ) "CAPTURE"))) { getargs( & tp, 5, (unsigned char * ) ","); int xs = 0, ys = 0; if (!XCLK) error("Camera not open"); int scale = 1; if (argc >= 1) { scale = getint(argv[0], 1, HRes / 160); if (argc == 5) { xs = getint(argv[2], 0, HRes - 1); ys = getint(argv[4], 0, VRes - 1); } else if (argc == 3) error("Syntax"); } for (int i = 0; OV7670_rgb[i].reg <= OV7670_REG_LAST; i++) { ov7670_set(OV7670_rgb[i].reg, OV7670_rgb[i].value); } char * buff = GetTempMemory(160 * 120 * 2); c.rgb = 0; disable_interrupts_pico(); capture(buff); enable_interrupts_pico(); char * linebuff = GetTempMemory(160 * 3 * scale); char * k = buff; for (int y = ys; y < 120 * scale + ys; y += scale) { int kk = 0; for (int x = 0; x < 160; x++) { c.rgbbytes[1] = * k++; c.rgbbytes[0] = * k++; for (int r = 0; r < scale; r++) { linebuff[kk++] = (c.rgbbytes[0] & 0x1F) << 3; linebuff[kk++] = (c.rgb & 0x07E0) >> 3; linebuff[kk++] = (c.rgbbytes[1] & 0xF8); } } for (int r = 0; r < scale; r++) { if (y + r < VRes) { int w = 160 * scale; if (w > HRes - xs) w = HRes - xs; DrawBuffer(xs, y + r, xs + w - 1, y + r, (unsigned char * ) linebuff); } } } } else if (checkstring(cmdline, (unsigned char * ) "CLOSE")) { cameraclose(); } else error("Syntax"); } #endif