有没有办法在 Nodemcu ESP8266 上提高 LUA 中 SD 卡的写入速度?

问题描述

我能达到的最大写入速度是 2.4 KB/s。有没有办法增加这个?

在 NodeMCU ESP8266 和 User_Modules.h 中的 SPI 模块上使用 LUA。 #define BUILD_FATFS 也在 user_config.h 中启用。

我有一个数据记录器,每次采样 920SPS 或 ~1.1ms/Sample 10 小时。 1.1 毫秒应该是很多时间将两个字节写入 SD 卡或样本之间的 xxx 字节缓冲区,但是我看到的最大写入速度是 498 毫秒写入 1200 字节或 7 毫秒写入 3 字节。这与 12.5MB/s 的 SD class 0 标准相差甚远。当我将 1200 B 转储到卡中时,记录器最终丢失了大约 450 个样本。


local adc1 = nil
local t_tbl={}
local n=1

function adcReady(_,_,c)
    
    _,adctbl[n],_ = adc1:read()
    n=n+1
    if n>400 then
    
        t_tbl[1]=tmr.Now()
        
        file.open("/SD0/sddata.txt","a")
        for k,v in ipairs(adctbl) do 
            file.write(v..",")
            adctbl[k]=nil
        end
        file.close()
        
        t_tbl[2]=tmr.Now()
        
        print(t_tbl[2] - t_tbl[1])
        n=1
        
    end
end

do
    local adc = {
        ADC1_ID             =   0,ADC1_ADDRESS        =   ads1115.ADDR_GND,GAIN                =   ads1115.GAIN_4_096V,SAMPLES             =   ads1115.DR_920SPS,CHANNEL             =   ads1115.SINGLE_0,MODE                =   ads1115.CONTINUOUS,CONV_READY          =   ads1115.CONV_RDY_1,}
    i2c.setup(i2c0.id,i2c0.sda,i2c0.scl,i2c0.speed)
    ads1115.reset()
    adc1 = ads1115.ads1015(adc.ADC1_ID,adc.ADC1_ADDRESS)   
    adc1:setting(adc.GAIN,adc.SAMPLES,adc.CHANNEL,adc.MODE,adc.CONV_READY)  
    
    spi.setup(1,spi.MASTER,spi.CPOL_LOW,spi.CPHA_LOW,8,2,spi.HALFDUPLEX)
    vol = file.mount("/SD0",8)   -- 2nd parameter is optional for non-standard SS/CS pin
    file.open("/SD0/sddata.txt","w+")
    file.close()
    
    tmr.create():alarm(1000,tmr.ALARM_SINGLE,function()
        gpio.mode(i2c0.conv_rdy,gpio.INT) 
        gpio.trig(i2c0.conv_rdy,'up',adcReady) --enable interrupt,active low rising edge==conv ready  
    end)
end

解决方法

您可以通过准备 2KB 对齐的文本块来加速文件写入。
将您的 adcReady 替换为:

local log_text = ""
local chunk_size = 2*1024

function adcReady(_,_,c)
   _,adctbl[n],_ = adc1:read()
   n = n + 1
   if n > 400 then
   
      t_tbl[1] = tmr.now()
      
      log_text = log_text..table.concat(adctbl,",1,n-1)..","
      local size = #log_text - #log_text % chunk_size
      local log_text_to_save = log_text:sub(1,size)
      log_text = log_text:sub(size + 1)
      
      t_tbl[2] = tmr.now()
      
      if size ~= 0 then 
         file.open("/SD0/sddata.txt","a")
         file.write(log_text_to_save)
         file.close()
      end
      
      t_tbl[3] = tmr.now()
      
      print(t_tbl[2] - t_tbl[1],t_tbl[3] - t_tbl[2])  -- for strings and GC,for File operations
      n = 1
      
   end
end

是否快于 498 毫秒?


更新:

带有缓存 tostring() 的新版本

local num2str = {}

function adcReady(_,_ = adc1:read()
   n = n + 1
   if n > 400 then

      t_tbl[1] = tmr.now()

      for i = 1,n - 1 do
         local v = adctbl[i]
         local s = num2str[v]
         if not s then
            s = v..","
            num2str[v] = s
         end
         adctbl[i] = s
      end
      local log_text_to_save = table.concat(adctbl,"",n-1)

      t_tbl[2] = tmr.now()

      file.open("/SD0/sddata.txt","a")
      file.write(log_text_to_save)
      file.close()

      t_tbl[3] = tmr.now()

      print(t_tbl[2] - t_tbl[1],for File operations
      n = 1

   end
end

它比以前的版本快吗?


更新 2:

local chr = string.char

function adcReady(_,n - 1 do
         local v = adctbl[i]
         -- 0<=v<=4095
         local s
         if v < 10 then
            s = chr(v + 48,44)
         else
            local m10 = v % 10
            if v < 100 then
               s = chr((v - m10)/10 + 48,m10 + 48,44)
            else
               local m100 = v % 100
               if v < 1000 then
                  s = chr((v - m100)/10 + 48,(m100 - m10)/10 + 48,44)
               else
                  local m1000 = v % 1000
                  s = chr((v - m1000)/1000 + 48,(m1000 - m100)/100 + 48,44)
               end
            end
         end
         adctbl[i] = s
      end
      local log_text_to_save = table.concat(adctbl,for File operations
      n = 1

   end
end

更新 3:

对于 Lua 5.3 和日志中的十六进制数字:

-- log output is in hex
local high = {} -- [1] = "",[2] = "1",...,[256] = "FF"
local low = {}  -- [1] = "0,[2] = "1,[16] = "F,"
for x = 0,255 do  -- replace 255 with 127 (to save memory) if ADC generates only positive values 0x0000-0x7FF0
   high[x+1] = string.format("%X",x*16):sub(1,-2)
   if x < 16 then
      low[x+1] = string.format("%X,x)
   end
end

-- in case of out-of-memory error reduce measures count (400) to 256
local measures = 400   -- recommended values are powers of 2
local measures_2 = measures*2

-- adctbl[] is not used anymore,text_buffer[] is used instead
local text_buffer = {}  -- array of (2*measures) elements
for x = 1,measures_2 do
   text_buffer[x] = ""
end

function adcReady(_,c)
   local _,v = adc1:read()
   -- 0x0000<=v<=0xFFF0
   text_buffer[n] = high[(v>>8)+1]
   text_buffer[n+1] = low[((v>>4)&15)+1]
   n = n + 2
   if n > measures_2 then

      t_tbl[1] = tmr.now()

      local log_text_to_save = table.concat(text_buffer,for File operations
      n = 1

   end
end

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...