Browse Source

add SPI transaction support

devsaurus 8 years ago
parent
commit
9cde0bbb83
5 changed files with 258 additions and 26 deletions
  1. 140 19
      app/driver/spi.c
  2. 4 0
      app/include/driver/spi.h
  3. 82 7
      app/modules/spi.c
  4. 26 0
      app/platform/platform.c
  5. 6 0
      app/platform/platform.h

+ 140 - 19
app/driver/spi.c

@@ -85,26 +85,27 @@ void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, unsigned databi
 		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode	
 	}
 
-	SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_DOUTDIN|SPI_USR_MOSI);
-
-    //set clock polarity
-    // TODO: This doesn't work
-    //if (cpol == 1) {
-    //    SET_PERI_REG_MASK(SPI_CTRL2(spi_no), (SPI_CK_OUT_HIGH_MODE<<SPI_CK_OUT_HIGH_MODE_S));
-    //} else {
-    //    SET_PERI_REG_MASK(SPI_CTRL2(spi_no), (SPI_CK_OUT_LOW_MODE<<SPI_CK_OUT_LOW_MODE_S));
-    //}
-    //os_printf("SPI_CTRL2 is %08x\n",READ_PERI_REG(SPI_CTRL2(spi_no)));
-
-    //set clock phase
-    if (cpha == 1) {
-    	SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE|SPI_CK_I_EDGE);
-    } else {
-    	CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE|SPI_CK_I_EDGE);
-    }
+	SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_DOUTDIN|SPI_USR_MOSI|
+			  SPI_RD_BYTE_ORDER|SPI_WR_BYTE_ORDER);
+
+	//set clock polarity
+	// TODO: This doesn't work
+	//if (cpol == 1) {
+	//    SET_PERI_REG_MASK(SPI_CTRL2(spi_no), (SPI_CK_OUT_HIGH_MODE<<SPI_CK_OUT_HIGH_MODE_S));
+	//} else {
+	//    SET_PERI_REG_MASK(SPI_CTRL2(spi_no), (SPI_CK_OUT_LOW_MODE<<SPI_CK_OUT_LOW_MODE_S));
+	//}
+	//os_printf("SPI_CTRL2 is %08x\n",READ_PERI_REG(SPI_CTRL2(spi_no)));
+
+	//set clock phase
+	if (cpha == 1) {
+		SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE|SPI_CK_I_EDGE);
+	} else {
+    		CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE|SPI_CK_I_EDGE);
+	}
 
-    CLEAR_PERI_REG_MASK(SPI_USER(HSPI), SPI_FLASH_MODE|SPI_WR_BYTE_ORDER|SPI_USR_MISO|
-                        SPI_RD_BYTE_ORDER|SPI_USR_ADDR|SPI_USR_COMMAND|SPI_USR_DUMMY);
+	CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE|SPI_USR_MISO|
+			    SPI_USR_ADDR|SPI_USR_COMMAND|SPI_USR_DUMMY);
 
 	//clear Daul or Quad lines transmission mode
 	CLEAR_PERI_REG_MASK(SPI_CTRL(spi_no), SPI_QIO_MODE|SPI_DIO_MODE|SPI_DOUT_MODE|SPI_QOUT_MODE);
@@ -151,6 +152,126 @@ void spi_mast_byte_write(uint8 spi_no, uint8 *data)
     *data = (uint8)(READ_PERI_REG(SPI_W0(spi_no))&0xff);
 }  
 
+/******************************************************************************
+ * FunctionName : spi_mast_set_mosi
+ * Description  : Enter provided data into MOSI buffer.
+ *                The data is regarded as a sequence of bits with length 'bitlen'.
+ *                It will be written left-aligned starting from position 'offset'.
+ * Parameters   :   uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
+ *                  uint8 offset - offset into MOSI buffer (number of bits)
+ *                  uint8 bitlen - valid number of bits in data
+ *                  uint32 data  - data to be written into buffer
+*******************************************************************************/
+void spi_mast_set_mosi(uint8 spi_no, uint8 offset, uint8 bitlen, uint32 data)
+{
+    uint8  wn, wn_offset, wn_bitlen;
+    uint32 wn_data;
+
+    if (spi_no > 1)
+        return; // handle invalid input number
+    if (bitlen > 32)
+        return; // handle invalid input number
+
+    while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR);
+
+    // determine which SPI_Wn register is addressed
+    wn = offset >> 5;
+    if (wn > 15)
+        return; // out of range
+    wn_offset = offset & 0x1f;
+    if (32 - wn_offset < bitlen)
+    {
+        // splitting required
+        wn_bitlen = 32 - wn_offset;
+        wn_data   = data >> (bitlen - wn_bitlen);
+    }
+    else
+    {
+        wn_bitlen = bitlen;
+        wn_data   = data;
+    }
+
+    do
+    {
+        // write payload data to SPI_Wn
+        SET_PERI_REG_BITS(REG_SPI_BASE(spi_no) +0x40 + wn*4, BIT(wn_bitlen) - 1, wn_data, 32 - (wn_offset + wn_bitlen));
+
+        // prepare writing of dangling data part
+        wn += 1;
+        wn_offset = 0;
+        if (wn <= 15)
+            bitlen -= wn_bitlen;
+        else
+            bitlen = 0; // force abort
+        wn_bitlen = bitlen;
+        wn_data   = data;
+    } while (bitlen > 0);
+}
+
+/******************************************************************************
+ * FunctionName : spi_mast_transaction
+ * Description  : Start a transaction and wait for completion.
+ * Parameters   :   uint8  spi_no       - SPI module number, Only "SPI" and "HSPI" are valid
+ *                  uint8  cmd_bitlen   - Valid number of bits in cmd_data.
+ *                  uint16 cmd_data     - Command data.
+ *                  uint8  addr_bitlen  - Valid number of bits in addr_data.
+ *                  uint32 addr_data    - Address data.
+ *                  uint8  mosi_bitlen  - Valid number of bits in MOSI buffer.
+ *                  uint8  dummy_bitlen - Number of dummy cycles.
+ *                  uint8  miso_bitlen  - number of bits to be captured in MISO buffer.
+*******************************************************************************/
+void spi_mast_transaction(uint8 spi_no, uint8 cmd_bitlen, uint16 cmd_data, uint8 addr_bitlen, uint32 addr_data,
+                          uint8 mosi_bitlen, uint8 dummy_bitlen, uint8 miso_bitlen)
+{
+    if (spi_no > 1)
+        return; // handle invalid input number
+
+    while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR);
+
+    // default disable COMMAND, ADDR, MOSI, DUMMY, MISO
+    CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND|SPI_USR_ADDR|SPI_USR_MOSI|SPI_USR_DUMMY|SPI_USR_MISO);
+    // default set bit lengths
+    WRITE_PERI_REG(SPI_USER1(spi_no),
+                   ((addr_bitlen - 1)  & SPI_USR_ADDR_BITLEN)    << SPI_USR_ADDR_BITLEN_S    |
+                   ((mosi_bitlen - 1)  & SPI_USR_MOSI_BITLEN)    << SPI_USR_MOSI_BITLEN_S    |
+                   ((dummy_bitlen - 1) & SPI_USR_DUMMY_CYCLELEN) << SPI_USR_DUMMY_CYCLELEN_S |
+                   ((miso_bitlen - 1)  & SPI_USR_MISO_BITLEN)    << SPI_USR_MISO_BITLEN_S);
+
+    // handle the transaction components
+    if (cmd_bitlen > 0)
+    {
+        uint16 cmd = cmd_data << (16 - cmd_bitlen); // align to MSB
+        cmd = (cmd >> 8) | (cmd << 8);              // swap byte order
+        WRITE_PERI_REG(SPI_USER2(spi_no),
+                       ((cmd_bitlen - 1 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) |
+                       (cmd & SPI_USR_COMMAND_VALUE));
+        SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND);
+    }
+    if (addr_bitlen > 0)
+    {
+        WRITE_PERI_REG(SPI_ADDR(spi_no), addr_data << (32 - addr_bitlen));
+        SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR);
+    }
+    if (mosi_bitlen > 0)
+    {
+        SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI);
+    }
+    if (dummy_bitlen > 0)
+    {
+        SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY);
+    }
+    if (miso_bitlen > 0)
+    {
+        SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO);
+    }
+
+    // start transaction
+    SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
+
+    while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR);
+}
+
+
 /******************************************************************************
  * FunctionName : spi_byte_write_espslave
  * Description  : SPI master 1 byte transmission function for esp8266 slave,

+ 4 - 0
app/include/driver/spi.h

@@ -22,6 +22,10 @@ void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, unsigned databi
 //use spi send 8bit data
 void spi_mast_byte_write(uint8 spi_no,uint8 *data);
 
+void spi_set_mosi(uint8 spi_no, uint8 offset, uint8 bitlen, uint32 data);
+void spi_mast_transaction(uint8 spi_no, uint8 cmd_bitlen, uint16 cmd_data, uint8 addr_bitlen, uint32 addr_data,
+                          uint8 mosi_bitlen, uint8 dummy_bitlen, uint8 miso_bitlen);
+
 //transmit data to esp8266 slave buffer,which needs 16bit transmission ,
 //first byte is master command 0x04, second byte is master data
 void spi_byte_write_espslave(uint8 spi_no,uint8 data);

+ 82 - 7
app/modules/spi.c

@@ -57,7 +57,7 @@ static int spi_send( lua_State *L )
 
   MOD_CHECK_ID( spi, id );
   if( lua_gettop( L ) < 2 )
-    return luaL_error( L, "wrong arg type" );
+    return luaL_error( L, "too few args" );
 
   for( argn = 2; argn <= lua_gettop( L ); argn ++ )
   {
@@ -68,7 +68,7 @@ static int spi_send( lua_State *L )
       numdata = ( int )luaL_checkinteger( L, argn );
       if( numdata < 0 || numdata > 255 )
         return luaL_error( L, "wrong arg range" );
-      platform_spi_send_recv( id, numdata );
+      platform_spi_transaction( id, 8, numdata, 0, 0, 0, 0, 0 );
       wrote ++;
     }
     else if( lua_istable( L, argn ) )
@@ -81,7 +81,7 @@ static int spi_send( lua_State *L )
         lua_pop( L, 1 );
         if( numdata < 0 || numdata > 255 )
           return luaL_error( L, "wrong arg range" );
-        platform_spi_send_recv( id, numdata );
+        platform_spi_transaction( id, 8, numdata, 0, 0, 0, 0, 0 );
       }
       wrote += i;
       if( i < datalen )
@@ -91,7 +91,7 @@ static int spi_send( lua_State *L )
     {
       pdata = luaL_checklstring( L, argn, &datalen );
       for( i = 0; i < datalen; i ++ )
-        platform_spi_send_recv( id, pdata[ i ] );
+          platform_spi_transaction( id, 8, pdata[ i ], 0, 0, 0, 0, 0 );
       wrote += i;
       if( i < datalen )
         break;
@@ -126,14 +126,89 @@ static int spi_recv( lua_State *L )
   return 1;
 }
 
+// Lua: spi.set_mosi( id, offset, bitlen, data1, [data2], ..., [datan] )
+static int spi_set_mosi( lua_State *L )
+{
+  unsigned id       = luaL_checkinteger( L, 1 );
+  unsigned offset   = luaL_checkinteger( L, 2 );
+  unsigned bitlen   = luaL_checkinteger( L, 3 );
+  unsigned argn;
+
+  MOD_CHECK_ID( spi, id );
+
+  if (offset < 0 || offset > 511) {
+    return luaL_error( L, "offset out of range" );
+  }
+  if (bitlen < 1 || bitlen > 32) {
+    return luaL_error( L, "bitlen out of range" );
+  }
+
+  if (lua_gettop( L ) < 4) {
+    return luaL_error( L, "too few args" );
+  }
+
+  for (argn = 4; argn <= lua_gettop( L ); argn++, offset += bitlen )
+  {
+    u32 data = luaL_checkinteger(L, argn );
+
+    if (offset + bitlen > 512)
+      return luaL_error( L, "data range exceeded > 512 bits" );
+
+    if (platform_spi_set_mosi( id, offset, bitlen, data ) != PLATFORM_OK)
+      return luaL_error( L, "failed" );
+  }
+
+  return 0;
+}
+
+// Lua: spi.transaction( id, cmd_bitlen, cmd_data, addr_bitlen, addr_data, mosi_bitlen, dummy_bitlen, miso_bitlen )
+static int spi_transaction( lua_State *L )
+{
+  unsigned id           = luaL_checkinteger( L, 1 );
+  unsigned cmd_bitlen   = luaL_checkinteger( L, 2 );
+  u32      cmd_data     = luaL_checkinteger( L, 3 );
+  unsigned addr_bitlen  = luaL_checkinteger( L, 4 );
+  u32      addr_data    = luaL_checkinteger( L, 5 );
+  unsigned mosi_bitlen  = luaL_checkinteger( L, 6 );
+  unsigned dummy_bitlen = luaL_checkinteger( L, 7 );
+  unsigned miso_bitlen  = luaL_checkinteger( L, 8 );
+
+  MOD_CHECK_ID( spi, id );
+
+  if (cmd_bitlen < 0 || cmd_bitlen > 16) {
+    return luaL_error( L, "cmd_bitlen out of range" );
+  }
+  if (addr_bitlen < 0 || addr_bitlen > 32) {
+    return luaL_error( L, "addr_bitlen out of range" );
+  }
+  if (mosi_bitlen < 0 || mosi_bitlen > 512) {
+    return luaL_error( L, "mosi_bitlen out of range" );
+  }
+  if (dummy_bitlen < 0 || dummy_bitlen > 256) {
+    return luaL_error( L, "dummy_bitlen out of range" );
+  }
+  if (miso_bitlen < 0 || miso_bitlen > 511) {
+    return luaL_error( L, "miso_bitlen out of range" );
+  }
+
+  if (platform_spi_transaction( id, cmd_bitlen, cmd_data, addr_bitlen, addr_data,
+                                mosi_bitlen, dummy_bitlen, miso_bitlen) != PLATFORM_OK)
+    return luaL_error( L, "failed" );
+
+  return 0;
+}
+
+
 // Module function map
 #define MIN_OPT_LEVEL   2
 #include "lrodefs.h"
 const LUA_REG_TYPE spi_map[] = 
 {
-  { LSTRKEY( "setup" ),  LFUNCVAL( spi_setup ) },
-  { LSTRKEY( "send" ),   LFUNCVAL( spi_send ) },
-  { LSTRKEY( "recv" ),   LFUNCVAL( spi_recv ) },
+  { LSTRKEY( "setup" ),       LFUNCVAL( spi_setup ) },
+  { LSTRKEY( "send" ),        LFUNCVAL( spi_send ) },
+  { LSTRKEY( "recv" ),        LFUNCVAL( spi_recv ) },
+  { LSTRKEY( "set_mosi" ),    LFUNCVAL( spi_set_mosi ) },
+  { LSTRKEY( "transaction" ), LFUNCVAL( spi_transaction ) },
 #if LUA_OPTIMIZE_MEMORY > 0
   { LSTRKEY( "MASTER" ),    LNUMVAL( PLATFORM_SPI_MASTER ) },
   { LSTRKEY( "SLAVE" ),     LNUMVAL( PLATFORM_SPI_SLAVE) },

+ 26 - 0
app/platform/platform.c

@@ -449,6 +449,32 @@ spi_data_type platform_spi_send_recv( unsigned id, spi_data_type data )
   return data;
 }
 
+int platform_spi_set_mosi( uint8_t id, uint8_t offset, uint8_t bitlen, spi_data_type data )
+{
+  if (offset + bitlen > 512)
+    return PLATFORM_ERR;
+
+  spi_mast_set_mosi( id, offset, bitlen, data );
+
+  return PLATFORM_OK;
+}
+
+int platform_spi_transaction( uint8_t id, uint8_t cmd_bitlen, spi_data_type cmd_data,
+                               uint8_t addr_bitlen, spi_data_type addr_data,
+                               uint8_t mosi_bitlen, uint8_t dummy_bitlen, uint8_t miso_bitlen )
+{
+  if ((cmd_bitlen   >  16) ||
+      (addr_bitlen  >  32) ||
+      (mosi_bitlen  > 512) ||
+      (dummy_bitlen > 256) ||
+      (miso_bitlen  > 512))
+    return PLATFORM_ERR;
+
+  spi_mast_transaction( id, cmd_bitlen, cmd_data, addr_bitlen, addr_data, mosi_bitlen, dummy_bitlen, miso_bitlen );
+
+  return PLATFORM_OK;
+}
+
 // ****************************************************************************
 // Flash access functions
 

+ 6 - 0
app/platform/platform.h

@@ -103,6 +103,12 @@ uint32_t platform_spi_setup( unsigned id, int mode, unsigned cpol, unsigned cpha
 spi_data_type platform_spi_send_recv( unsigned id, spi_data_type data );
 void platform_spi_select( unsigned id, int is_select );
 
+int platform_spi_set_mosi( uint8_t id, uint8_t offset, uint8_t bitlen, spi_data_type data );
+int platform_spi_transaction( uint8_t id, uint8_t cmd_bitlen, spi_data_type cmd_data,
+                              uint8_t addr_bitlen, spi_data_type addr_data,
+                              uint8_t mosi_bitlen, uint8_t dummy_bitlen, uint8_t miso_bitlen );
+
+
 // *****************************************************************************
 // UART subsection