【工作筆記】IOT.js適配AWorks平臺(tái)通用外設(shè)接口(6):UART

一、前言

近期因工作需求學(xué)習(xí)了一下 IOT.js 和 AWorks 平臺(tái)通用外設(shè)接口(包括:ADC、GPIO、I2C、PWM、SPI 和 UART),并將它們逐一適配到 IOT.js 中,為后續(xù) AWTK-MVMM 的 JS項(xiàng)目支持平臺(tái)外設(shè)調(diào)用奠定基礎(chǔ),此處做筆記記錄一下。

備注:IOT.js 和 AWorks 的相關(guān)介紹請(qǐng)看第一篇 ADC 適配筆記。

二、UART

2.1 UART總線

UART(Universal Asynchronous Receiver/Transmitter)是一種通用異步收發(fā)傳輸器,其使用串行的方式在雙機(jī)之間進(jìn)行數(shù)據(jù)交換,實(shí)現(xiàn)全雙工通信,數(shù)據(jù)引腳僅包含用于接收數(shù)據(jù)的RXD和用于發(fā)送數(shù)據(jù)的TXD。數(shù)據(jù)在數(shù)據(jù)線上按一位一位的串行傳輸,要正確解析這些數(shù)據(jù),必須遵循UART協(xié)議,作為了解,這里僅簡(jiǎn)要講述幾個(gè)關(guān)鍵的概念:

  • 波特率:它決定了數(shù)據(jù)傳輸?shù)乃俾?,其表示每秒傳?shù)據(jù)的位數(shù),值越大,數(shù)據(jù)通信的速率越高,數(shù)據(jù)傳輸?shù)迷娇?。常見的波特率有?800、9600、14400、19200、38400、115200等等,若波特率為115200,則表示每秒鐘可以傳輸115200位(bit)數(shù)據(jù)。

  • 空閑位:數(shù)據(jù)線上沒有數(shù)據(jù)傳輸時(shí),數(shù)據(jù)線處于空閑狀態(tài)??臻e狀態(tài)的電平邏輯為”1“。

  • 起使位:它表示一幀數(shù)據(jù)傳輸?shù)拈_始,起使位的電平邏輯是”0“。

  • 數(shù)據(jù)包:緊接起始位后,即位實(shí)際通信傳輸?shù)臄?shù)據(jù),數(shù)據(jù)的位數(shù)可以是5、6、7、8等,數(shù)據(jù)傳輸時(shí),從最低位開始依次傳輸。

  • 奇偶校驗(yàn)位:奇偶校驗(yàn)位用于接受方對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),即使發(fā)現(xiàn)由于通信故障等問題造成的錯(cuò)誤數(shù)據(jù),它是可選的,可以不適用奇偶校驗(yàn)位。

  • 停止位:它表示一幀數(shù)據(jù)的結(jié)束,其電平邏輯為”1“,其寬度可以是1位、1.5位、2位。即其持續(xù)的時(shí)間為位數(shù)乘以傳輸一位的時(shí)間(由波特率決定),例如波特率位115200,則傳輸一位的時(shí)間為1/115200秒,約為8.68us。若停止位的寬度為1.5位,則表示停止位持續(xù)時(shí)間位:1.5 * 8.68 us,約等于 13 us。

常見的幀格式位:1位起始位,8位數(shù)據(jù)位,無校驗(yàn),1位停止位。由于起使位的寬度恒為1位,不會(huì)變化,而數(shù)據(jù)位,校驗(yàn)位和停止位都是可變的,因此,往往在描述串口通信協(xié)議時(shí),都只是描述其波特率、數(shù)據(jù)位,校驗(yàn)位和停止位,不再單獨(dú)說明起使位。

注意:通信雙方必須使用完全相同的協(xié)議,包括波特率、起始位、數(shù)據(jù)位、停止位等。如果協(xié)議不一致,則通信數(shù)據(jù)會(huì)錯(cuò)亂,不能正常通信。再通信中,若出現(xiàn)亂碼的情況,應(yīng)該首先檢查通信雙方所使用的協(xié)議是否一致。

2.2 串行接口

在AWorks中,定義了通用的串行接口,可以使用串行接口操作UART,實(shí)現(xiàn)數(shù)據(jù)的收發(fā),相關(guān)接口如下:

  • aw_serial_ioctl:UART控制。
  • aw_serial_write:發(fā)送數(shù)據(jù)。
  • aw_serial_read:接收數(shù)據(jù)。

三、適配過程

3.1 AWorks演示代碼

先來看看這些UART相關(guān)接口的基本用法,我們?cè)诘装迳吓芤幌潞?jiǎn)單的例程。

步驟一:外設(shè)使能,在AWorks工程配置文件 aw_prj_params.h 中開啟以下宏定義使能COM0串口:

 #define AW_DEV_IMX1050_LPUART1 /**< \brief iMX1050 LPUART1 (COM0) */

步驟二:到外設(shè)文件中查看設(shè)備對(duì)應(yīng)的引腳,比如這里查看 awbl_hwconf_imx1050_lpuart1.h 文件,可以看到該設(shè)備的使用的引腳為GPIO1_12GPIO1_13引腳,在底板上分別對(duì)應(yīng)RXD和TXD引腳,我們需確定這些引腳能正常使用。

步驟三:編寫例程,測(cè)試COM0串口的讀寫功能。示例代碼如下:


#include "aworks.h"
#include "aw_delay.h"
#include "aw_serial.h"
#include "aw_ioctl.h"

int main() {
#define  TEST_SERIAL_NUM   COM0

    char    buf[32];
    int     len = 0;
    int     i   = 0;

    /* 串口初始化配置 波特率115200 */
    aw_serial_ioctl(TEST_SERIAL_NUM, SIO_BAUD_SET, (void *)115200);

    /* 串口參數(shù) :8個(gè)數(shù)據(jù)位 1個(gè)停止位,無奇偶校驗(yàn) */
    aw_serial_ioctl(TEST_SERIAL_NUM, SIO_HW_OPTS_SET, (void *)(CS8 | CLOCAL | CREAD));

    /* 串口模式 :查詢模式 發(fā)送字符串 */
    aw_serial_ioctl(TEST_SERIAL_NUM, SIO_MODE_SET, (void *)SIO_MODE_POLL);
    aw_serial_poll_write(TEST_SERIAL_NUM, "Hello,Enter Serial Poll Mode:\r\n", 32);
    
    for(i = 0; i < 10; i++) {
        len = aw_serial_poll_read(TEST_SERIAL_NUM, buf, 10);
        if (len > 0) {
            aw_serial_poll_write(TEST_SERIAL_NUM, buf, len);
        }
        aw_serial_poll_write(TEST_SERIAL_NUM, "\r\n", len);
    }

    /*
     * 再次配置串口模式 :中斷模式
     *
     */
    // 清空串口輸入輸出緩沖區(qū)
    aw_serial_ioctl(TEST_SERIAL_NUM, AW_FIOFLUSH, NULL);
    // 設(shè)置串口讀取超時(shí)時(shí)間
    aw_serial_ioctl(TEST_SERIAL_NUM, AW_TIOCRDTIMEOUT, (void *)10);
    // 設(shè)置為中斷模式
    aw_serial_ioctl(TEST_SERIAL_NUM, SIO_MODE_SET, (void *)SIO_MODE_INT);

    // 發(fā)送數(shù)據(jù)
    aw_serial_write(TEST_SERIAL_NUM, "Hello,Enter Serial INT  Mode:\r\n", 32);
    for(i = 0; i < 10; i++) {
        // 讀取數(shù)據(jù)
        len = aw_serial_read(TEST_SERIAL_NUM, buf, 10);
        if (len > 0) {
            aw_serial_write(TEST_SERIAL_NUM, buf, len);
        }
        aw_serial_write(TEST_SERIAL_NUM, "\r\n", len);
    }

    for (;;) {
        aw_mdelay(1000);
    }
    return 0;
}

3.2 C語言適配層

在 IOT.js 中,適配某個(gè)平臺(tái)的外設(shè)通常需要實(shí)現(xiàn) src/modules/iotjs_module_xxx.h 文件中的接口,比如這里我們需要實(shí)現(xiàn) iotjs_module_uart.h 中的相關(guān)接口:

#ifndef IOTJS_MODULE_UART_H
#define IOTJS_MODULE_UART_H

#include "iotjs_def.h"
#include "iotjs_module_periph_common.h"

#ifndef UART_WRITE_BUFFER_SIZE
#define UART_WRITE_BUFFER_SIZE 512
#endif

typedef struct iotjs_uart_platform_data_s iotjs_uart_platform_data_t;

typedef struct {
  int device_fd;
  unsigned baud_rate;
  uint8_t data_bits;
  iotjs_string_t buf_data;
  unsigned buf_len;
  char* buf;
  iotjs_uart_platform_data_t* platform_data;
} iotjs_uart_t;

void iotjs_uart_handle_close_cb(uv_handle_t* handle);
void iotjs_uart_register_read_cb(uv_poll_t* uart_poll_handle);

void iotjs_uart_create_platform_data(iotjs_uart_t* uart);
jerry_value_t iotjs_uart_set_platform_config(iotjs_uart_t* uart,
                                             const jerry_value_t jconfig);
void iotjs_uart_destroy_platform_data(iotjs_uart_platform_data_t* pdata);

bool iotjs_uart_open(uv_handle_t* uart_poll_handle);
bool iotjs_uart_write(uv_handle_t* uart_poll_handle);

#endif /* IOTJS_MODULE_UART_H */

適配層(src/modules/aworks/iotjs_module_uart-aworks.c)代碼如下:

#if !defined(WITH_AWORKS)
#error "Module __FILE__ is for AWorks only"
#endif

#include "uv.h"
#include "iotjs_def.h"
#include "iotjs_uv_handle.h"
#include "modules/iotjs_module_uart.h"
#include "aw_serial.h"
#include "aw_delay.h"
#include "aw_ioctl.h"

#ifndef AWORKS_UART_WRITE_BUFFER_SIZE
#define AWORKS_UART_WRITE_BUFFER_SIZE 127
#endif

struct iotjs_uart_platform_data_s {
  iotjs_string_t device;
  uint32_t wait_time;
  int32_t buf_len;
  uv_mutex_t mutex;
  char buf[UART_WRITE_BUFFER_SIZE];
};

static int device_to_constant(const char* device) {
  int ret = -1;

  if (!strcmp(device, "COM0")) {
    ret = COM0;
  } else if (!strcmp(device, "COM1")) {
    ret = COM1;
  } else if (!strcmp(device, "COM2")) {
    ret = COM2;
  } else if (!strcmp(device, "COM3")) {
    ret = COM3;
  } else if (!strcmp(device, "COM4")) {
    ret = COM4;
  } else if (!strcmp(device, "COM5")) {
    ret = COM5;
  } else if (!strcmp(device, "COM6")) {
    ret = COM6;
  } else if (!strcmp(device, "COM7")) {
    ret = COM7;
  } else if (!strcmp(device, "COM8")) {
    ret = COM8;
  } else if (!strcmp(device, "COM9")) {
    ret = COM9;
  }

  return ret;
}

static int databits_to_constant(uint8_t dataBits) {
  switch (dataBits) {
    case 8:
      return CS8;
    case 7:
      return CS7;
    case 6:
      return CS6;
    case 5:
      return CS5;
  }
  return -1;
}

void iotjs_uart_create_platform_data(iotjs_uart_t* uart) {
  uart->platform_data = IOTJS_ALLOC(iotjs_uart_platform_data_t);
}

void iotjs_uart_destroy_platform_data(iotjs_uart_platform_data_t* pdata) {
  IOTJS_ASSERT(pdata);

  uv_mutex_destroy(&pdata->mutex);
  iotjs_string_destroy(&pdata->device);
  IOTJS_RELEASE(pdata);
}

jerry_value_t iotjs_uart_set_platform_config(iotjs_uart_t* uart,
                                             const jerry_value_t jconfig) {
  JS_GET_REQUIRED_CONF_VALUE(jconfig, uart->platform_data->device,
                             IOTJS_MAGIC_STRING_DEVICE, string);

  return jerry_create_undefined();
}

static int uv_poll_uart_try_read(uv_poll_t* handle) {
  char buf[512];
  int32_t len = 0, buf_len = 0, buf_start = 0;
  iotjs_uart_t* uart = (iotjs_uart_t*)IOTJS_UV_HANDLE_EXTRA_DATA(handle);

  uv_mutex_lock(&uart->platform_data->mutex);
  buf_len = min(UART_WRITE_BUFFER_SIZE - uart->platform_data->buf_len, sizeof(buf));
  uv_mutex_unlock(&uart->platform_data->mutex);


  if (buf_len > 0) {
    len = aw_serial_read(uart->device_fd, buf, buf_len);
    
    if (len > 0) {
      uv_mutex_lock(&uart->platform_data->mutex);
      buf_start = uart->platform_data->buf_len;
      assert(buf_start + len < UART_WRITE_BUFFER_SIZE);
      memcpy(uart->platform_data->buf + buf_start, buf, len);
      uart->platform_data->buf_len += len;
      uv_mutex_unlock(&uart->platform_data->mutex);
    } else {
      aw_mdelay(uart->platform_data->wait_time);
    }
  } else {
    aw_mdelay(1);
  }

  if (len > 0) {
    return 0;
  } else {
    return 1;
  }
}

bool iotjs_uart_open(uv_handle_t* uart_poll_handle) {
  aw_err_t ret;
  struct aw_serial_timeout  timeout;
  uv_poll_t* uv_poll_handle = (uv_poll_t*)uart_poll_handle;
  iotjs_uart_t* uart =
      (iotjs_uart_t*)IOTJS_UV_HANDLE_EXTRA_DATA(uart_poll_handle);
  int com = device_to_constant(iotjs_string_data(&uart->platform_data->device));
  int data_bits = databits_to_constant(uart->data_bits);

  if (com < 0 || data_bits < 0) {
    DLOG("%s: serial port number error or data_bits error(%d)", __func__, com);
    return false;
  }

  /* 設(shè)置串口波特率 */
  ret = aw_serial_ioctl(com, SIO_BAUD_SET, (void*)uart->baud_rate);
  if (ret != AW_OK) {
    DLOG("%s: cannot set baud rate(%d)", __func__, ret);
    return false;
  }

  /* 設(shè)置數(shù)據(jù)位數(shù) */
  ret = aw_serial_ioctl(com, SIO_HW_OPTS_SET, (void*)data_bits);
  if (ret != AW_OK) {
    DLOG("%s: cannot set data bits(%d)", __func__, ret);
    return false;
  }

  timeout.rd_timeout = aw_ms_to_ticks(100);             /* 讀總超時(shí)為100ms */
  timeout.rd_interval_timeout = aw_ms_to_ticks(100);    /* 碼間超時(shí)為100ms */
  ret = aw_serial_timeout_set(com, &timeout);
  if (ret != AW_OK) {
    DLOG("%s: cannot set timeout(%d)", __func__, ret);
    return false;
  }
  uart->device_fd = com;
  iotjs_uart_register_read_cb(uv_poll_handle);

  uart->platform_data->buf_len = 0;
  uv_mutex_init(&uart->platform_data->mutex);
  memset(uart->platform_data->buf, 0x0, sizeof(uart->platform_data->buf));
  uv_poll_set_try_poll_function(uv_poll_handle, uv_poll_uart_try_read);
  uart->platform_data->wait_time = AWORKS_UART_WRITE_BUFFER_SIZE / (uart->baud_rate / 10 / 1000 + 1) / 2; 

  return true;
}

bool iotjs_uart_write(uv_handle_t* uart_poll_handle) {
  int ret = 0;
  iotjs_uart_t* uart =
      (iotjs_uart_t*)IOTJS_UV_HANDLE_EXTRA_DATA(uart_poll_handle);
  int com = device_to_constant(iotjs_string_data(&uart->platform_data->device));
  const char* buf_data = iotjs_string_data(&uart->buf_data);

  DDDLOG("%s - data: %s", __func__, buf_data);

  ret = aw_serial_write(com, buf_data, uart->buf_len);

  if (ret < 0) {
    DLOG("%s: write data failed(%d)", __func__, ret);
    return false;
  }

  return true;
}

void iotjs_uart_handle_close_cb(uv_handle_t* uart_poll_handle) {
  iotjs_uart_t* uart =
      (iotjs_uart_t*)IOTJS_UV_HANDLE_EXTRA_DATA(uart_poll_handle);
  iotjs_uart_destroy_platform_data(uart->platform_data);
}

int uart_read(iotjs_uart_t* uart, void* dst, unsigned int n) {
  int32_t buf_len = 0;
  uv_mutex_lock(&uart->platform_data->mutex);
  if (uart->platform_data->buf_len > 0) {
    buf_len = min(n, uart->platform_data->buf_len);
    assert(buf_len >= 0);

    memcpy(dst, uart->platform_data->buf, buf_len);
    uart->platform_data->buf_len -= buf_len;
    if (uart->platform_data->buf_len > 0) {
      memcpy(uart->platform_data->buf, uart->platform_data->buf + buf_len, uart->platform_data->buf_len);
    }

    assert(uart->platform_data->buf_len >= 0);
  }
  uv_mutex_unlock(&uart->platform_data->mutex);
  return buf_len;
}

3.2 JS測(cè)試代碼

適配好后,我們編寫 JS 代碼測(cè)試一下:

var uart = require('uart');

var configuration = {
  device: 'COM2', /* 串口3使用GPIO1_22和GPIO1_23引腳,在底板上分別對(duì)應(yīng)RX3和TX3引腳 */
  baudRate: 115200,
  dataBits: 8,
};

var read = 0;
var write = 0;
var serial = uart.open(configuration, function (err) {
  console.log('open done');

  serial.on('data', function (data) {
    console.log('read result: ' + data.toString());
    read = 1;

    if (read && write) {
      serial.close(function (err) {
        if (err) {
          console.log('Have an error: ' + err.message);
        }
        console.log('on read data callback close done');
      });
    }
  });

  serial.write('Hello there?\n\r', function (err) {
    console.log('write done');
    write = 1;

    if (read && write) {
      console.log('on read data callback close start');
      serial.close(function (err) {
        if (err) {
          console.log('Have an error: ' + err.message);
        }
        console.log('on write data callback close done');
      });
    }
  });
});

輸出結(jié)果:

open done
write done
read result: Hello there?
on read data callback close done
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容