feat: support 8gray. (#2)
BIN
doc/pic_gray8.jpg
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
53
readme.md
@@ -1,15 +1,24 @@
|
||||
# cc2640r2-etag
|
||||
|
||||
cc2640r2 电子标签改电子时钟固件.
|
||||
> 显示时钟日历,或静态图片。
|
||||
|
||||
目前支持,
|
||||
* cc2640r2l_2in13_ssd1680_250x122
|
||||
* cc2640r2l_2in9_ssd1680a_296x128
|
||||
* cc2640r2l_2in9_ssd1680a_296x128 **主要支持**
|
||||
|
||||

|
||||
|
||||
单节或者双节 CR2450 电池供电.
|
||||
|
||||
### 8级灰度支持
|
||||
|
||||
通过控制 LUT,仅保留 Black 和 Red 的刷新,控制刷新电压及时间,在多次刷新下可获得灰度图片。
|
||||
|
||||

|
||||
|
||||
tools 下提供 bwr_gray8.act 调色板,便于在 Photoshop 里生成 BWR 色彩的8级灰度图像。
|
||||
|
||||
## 编译
|
||||
|
||||
IAR 9.40 或 CCS 12.5
|
||||
@@ -22,7 +31,9 @@ SDK: simplelink_cc2640r2_sdk_1_40_00_45
|
||||
|
||||
电子标签为 cjtag 2 wire, 需要 xds 或者 jlink 烧写器.
|
||||
|
||||
#### cc2640r2l_2in9_ssd1680a_296x128
|
||||
> 可自制 XDS110 烧写器
|
||||
|
||||
### cc2640r2l_2in9_ssd1680a_296x128
|
||||
|
||||

|
||||
|
||||
@@ -34,9 +45,15 @@ SDK: simplelink_cc2640r2_sdk_1_40_00_45
|
||||
|4|TMS|
|
||||
|7|NRST|
|
||||
|
||||
### 保留 SNV 数据
|
||||
|
||||
固件配置 SNV (非易失存储器) 为单页模式,数据保存在 FLash 中第 30 页中。
|
||||
|
||||
在首次烧录后,如需保留 LUT 等 SNV 设置,请在烧写器 (如 SmartRF Flash Programmer 2) 跳过第 30 页即可,设置即可保留。
|
||||
|
||||
## 低功耗蓝牙 BLE5
|
||||
|
||||
电子标签使用 BLE5 配置, 兼容 4.2.
|
||||
电子标签可使用 BLE5 进行配置。
|
||||
|
||||
可使用 tools 下 cc2640r2_etag.html (chrome 蓝牙) 配置, 或手机端 nRF connect App 配置.
|
||||
|
||||
@@ -48,3 +65,33 @@ GATT 配置,
|
||||
- UUID: FFF2, 时区偏移分钟, int32, 默认为北京时区 (+8*60)
|
||||
- UUID: FFF3, 电池电压 mV, uint16
|
||||
- UUID: FFF4, 温度 摄氏度, int8, (-127 ~ +128)
|
||||
- UUID: FFF5, RTC 微调, int8, (-5 ~ +5)
|
||||
- UUID: FFFE, RxTx 服务, 图片模式、灰度刷新、内置LUT更新等
|
||||
|
||||
### Advertising
|
||||
|
||||
本固件通过 BLE Adv 中的 Service Data 通告当前 etag 的数据,格式如下
|
||||
|
||||
```
|
||||
UUID, MAC address, Display Mode, Unix Epoch Time, Temperature, Battery Level.
|
||||
```
|
||||
|
||||
通告间隔为 1s。
|
||||
|
||||
可通过 ble5_ctrl.py 批量获得近距离 etag 的信息。
|
||||
|
||||
## Webtools
|
||||
|
||||
当前配置能力由 cc2640r2_etag.html 完成,需使用支持 BLE5 蓝牙的 Chrome/Edge 浏览器打开。
|
||||
|
||||
时钟模式功能,
|
||||
1. 获取当前 etag 的信息
|
||||
2. 一键设置当前时间、当前时区、RTC 微调等
|
||||
|
||||
图片模式,
|
||||
1. 选择 BWR 三色图像 (默认刷新,15秒左右)
|
||||
2. 选择 BWR 灰度图像 (8级灰度刷新,2分钟左右)
|
||||
|
||||
SNV 功能,
|
||||
1. 保存当前设置 (时区、RTC 微调,模式等)
|
||||
2. 保存和查看自定义 LUT (可定义 3 个LUT)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <time.h> // time
|
||||
#include <stdint.h> // uint8_t
|
||||
#include <string.h> // memset
|
||||
|
||||
// OBD
|
||||
#include "OneBitDisplay.h"
|
||||
@@ -27,6 +28,7 @@ OBDISP obd;
|
||||
|
||||
extern const uint8_t ucMirror[];
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* <int.frac> format size (3.8) bits.
|
||||
* int for 0-3 voltage
|
||||
@@ -35,6 +37,7 @@ extern const uint8_t ucMirror[];
|
||||
#define INTFRAC_V(x) (x>>8)
|
||||
#define INTFRAC_mV(x) ((x&0xff)*1000/256)
|
||||
#define INTFRAC2MV(x) (INTFRAC_mV(x)+(INTFRAC_V(x)*1000))
|
||||
#endif
|
||||
|
||||
// https://www.mdpi.com/2072-666X/12/5/578
|
||||
#define _VS(x) x<<6
|
||||
@@ -43,6 +46,9 @@ extern const uint8_t ucMirror[];
|
||||
#define VSL _VS(0b10)
|
||||
#define VSH2 _VS(0b11)
|
||||
|
||||
#if 0
|
||||
/* the LUT lots of values are zeros, use a lite version instead.
|
||||
*/
|
||||
static const uint8_t lut_full_bwr[] = {
|
||||
// 0: LUTC x 7
|
||||
// RP A B C D SRAB SRCD
|
||||
@@ -55,7 +61,7 @@ static const uint8_t lut_full_bwr[] = {
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
// 56: LUTR x 7
|
||||
0x1, VSL|0x2f, 0x0, VSH2|0x3f, 0x0, 0x1, 0x0,
|
||||
0x1, VSL|0x2f, 0x0, VSH2|0x3f, 0x0, 0x1, 0xa,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
@@ -117,6 +123,124 @@ static void EPD_2IN9_Lut(const unsigned char *lut)
|
||||
EPD_SSD_SendCommand(0x2C);
|
||||
EPD_SSD_SendData(*(lut+232));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* LUT lite is small than LUT used in SSD1680A, only 35 bytes.
|
||||
* lut_lite_fast_bw is default lut for clock display,
|
||||
* lut_lite_gray8_bwr is default lut for BLE display.
|
||||
*/
|
||||
#define LUT_LITE_LEN 35
|
||||
|
||||
// LUT for clock fast display. (only black/white)
|
||||
static const uint8_t lut_lite_fast_bw[LUT_LITE_LEN] = {
|
||||
// RP A B C D SRAB SRCD
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // LUTC
|
||||
0x1, VSL|0x2f, 0x0, VSH2|0x3f, 0x0, 0x1, 0x0, // LUTR
|
||||
0x1, VSL|0x3f, 0x0, 0x0, 0x0, 0x1, 0x0, // LUTW
|
||||
0x1, VSH1|0x2f, 0x0, 0x0, 0x0, 0x1, 0x0, // LUTB
|
||||
|
||||
// FR
|
||||
0x04, // 2: 50hz, 3: 75Hz, 4: 100Hz, 5: 125Hz
|
||||
|
||||
// EOPT VGH VSH1 VSH2 VSL VCOM
|
||||
// 3F 03 04 2C
|
||||
// 22 -20v 15v 3v -15v
|
||||
0x22, 0x17, 0x41, 0x94, 0x32, 0x36
|
||||
};
|
||||
|
||||
// LUT for BLE Gray display (8 steps)
|
||||
static const uint8_t lut_lite_gray8_bwr[LUT_LITE_LEN] = {
|
||||
// RP A B C D SRAB SRCD
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // LUTC
|
||||
0x1, VSH2|0x3f, 0x0, 0x0, 0x0, 0x1, 0x0, // LUTR
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // LUTW
|
||||
0x1, VSH1|0x03, 0x0, 0x0, 0x0, 0x1, 0x0, // LUTB
|
||||
|
||||
// FR
|
||||
0x04, // 2: 50hz, 3: 75Hz, 4: 100Hz, 5: 125Hz
|
||||
|
||||
// EOPT VGH VSH1 VSH2 VSL VCOM
|
||||
// 3F 03 04 2C
|
||||
// 22 -20v 15v 3.0v -15v
|
||||
0x22, 0x17, 0x41, 0x94, 0x32, 0x36
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void EPD_2IN9_Lut(const unsigned char *lut)
|
||||
{
|
||||
// SSD1680A uses 233 LUT.
|
||||
|
||||
// 0, wareform setting
|
||||
EPD_SSD_SendCommand(0x32);
|
||||
for (int j=0; j<4; j++) { // LUT0-3
|
||||
// wave setting
|
||||
for (int i=0; i<7; i++) {
|
||||
EPD_SSD_SendData(lut[j*7+i]);
|
||||
}
|
||||
// no config
|
||||
for (int i=0; i<7*7; i++) {
|
||||
EPD_SSD_SendData(0x00);
|
||||
}
|
||||
}
|
||||
// 4 * (7+49) = 224
|
||||
|
||||
// 224, FR
|
||||
// 2: 50hz, 3: 75Hz, 4: 100Hz, 5: 125Hz
|
||||
EPD_SSD_SendData(lut[28]);
|
||||
|
||||
// 225, XON
|
||||
EPD_SSD_SendData(0x00);
|
||||
EPD_SSD_SendData(0x00);
|
||||
|
||||
// EOPT VGH VSH1 VSH2 VSL VCOM
|
||||
// 3F 03 04 2C
|
||||
// 22 -20v 15v 3v -15v
|
||||
// 0x22, 0x17, 0x41, 0x94, 0x32, 0x36
|
||||
|
||||
// 227, gate voltage
|
||||
EPD_SSD_SendCommand(0x3F);
|
||||
EPD_SSD_SendData(lut[29]);
|
||||
|
||||
EPD_SSD_SendCommand(0x03);
|
||||
EPD_SSD_SendData(lut[30]);
|
||||
|
||||
// 229, source voltage
|
||||
EPD_SSD_SendCommand(0x04);
|
||||
EPD_SSD_SendData(lut[31]); // VSH
|
||||
EPD_SSD_SendData(lut[32]); // VSH2
|
||||
EPD_SSD_SendData(lut[33]); // VSL
|
||||
|
||||
// 232, VCOM
|
||||
EPD_SSD_SendCommand(0x2C);
|
||||
EPD_SSD_SendData(lut[34]);
|
||||
}
|
||||
|
||||
static void EPD_2IN9_Lut_ById(int idx)
|
||||
{
|
||||
uint8_t buf[LUT_LITE_LEN];
|
||||
const uint8_t *lut = buf;
|
||||
|
||||
int rc = EPD_SNV_LoadLut(idx, buf, LUT_LITE_LEN);
|
||||
if (rc != SUCCESS) {
|
||||
// load default if no lut found.
|
||||
switch (idx) {
|
||||
case 0:
|
||||
lut = lut_lite_fast_bw;
|
||||
break;
|
||||
case 1:
|
||||
lut = lut_lite_gray8_bwr;
|
||||
break;
|
||||
default:
|
||||
lut = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lut) {
|
||||
EPD_2IN9_Lut(lut);
|
||||
}
|
||||
}
|
||||
|
||||
static void EPD_2IN9_SoftReset()
|
||||
{
|
||||
@@ -244,18 +368,48 @@ void EPD_2IN9_Sleep(void)
|
||||
EPD_SSD_SendData(0x01); // 01: mode 1, 11: mode 2
|
||||
}
|
||||
|
||||
void EPD_SSD_Update(void)
|
||||
void EPD_2IN9_Clear(void)
|
||||
{
|
||||
static time_t last = 0;
|
||||
// wakeup EPD
|
||||
EPD_SSD_Reset();
|
||||
|
||||
time_t now = time(NULL);
|
||||
if (last && ((now % 60) != 0)) {
|
||||
// soft reset
|
||||
EPD_2IN9_SoftReset();
|
||||
|
||||
// clear
|
||||
EPD_2IN9_BWR(EPD_WIDTH, EPD_HEIGHT, 0, 0);
|
||||
EPD_2IN9_WriteRam(NULL, EPD_WIDTH, EPD_HEIGHT, 0, 0, 0);
|
||||
EPD_2IN9_WriteRam(NULL, EPD_WIDTH, EPD_HEIGHT, 0, 0, 1);
|
||||
|
||||
// display
|
||||
EPD_2IN9_Display(0xf7); // c7: by REG f7: by OTP b1: no display
|
||||
|
||||
// wait & sleep
|
||||
EPD_SSD_WaitBusy(15 * 1000);
|
||||
EPD_2IN9_Sleep();
|
||||
}
|
||||
|
||||
void EPD_2IN9_Update_Clock(void)
|
||||
{
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
if (clock_last && ((now % 60) != 0)) {
|
||||
return;
|
||||
}
|
||||
last = now;
|
||||
|
||||
// adjust TZ offset
|
||||
now += utc_offset_mins * 60;
|
||||
|
||||
// get localtime
|
||||
struct tm *l = localtime(&now);
|
||||
|
||||
// full update on first start
|
||||
bool full_upd = (clock_last == 0 || l->tm_min == 0) ? true : false;
|
||||
|
||||
// clock started.
|
||||
clock_last = 1;
|
||||
|
||||
// wakeup EPD
|
||||
EPD_SSD_Reset();
|
||||
|
||||
@@ -299,9 +453,8 @@ void EPD_SSD_Update(void)
|
||||
}
|
||||
|
||||
// full update every 30 mins
|
||||
bool full_upd = (l->tm_min == 0) ? true : false;
|
||||
EPD_2IN9_BWR(EPD_WIDTH, EPD_HEIGHT, 0, 0);
|
||||
if (!full_upd) EPD_2IN9_Lut(lut_full_bwr);
|
||||
if (!full_upd) EPD_2IN9_Lut_ById(0);
|
||||
EPD_2IN9_WriteRam(epd_buffer, EPD_WIDTH, EPD_HEIGHT, 0, 0, 0);
|
||||
EPD_2IN9_WriteRam(NULL, EPD_WIDTH, EPD_HEIGHT, 0, 0, 1);
|
||||
|
||||
@@ -314,8 +467,98 @@ void EPD_SSD_Update(void)
|
||||
return;
|
||||
}
|
||||
|
||||
void EPD_2IN9_Update_Image()
|
||||
{
|
||||
//static uint8_t last_step = 0;
|
||||
uint8_t step = epd_step;
|
||||
|
||||
switch (step) {
|
||||
case EPD_CMD_CLR:
|
||||
EPD_2IN9_Clear();
|
||||
break;
|
||||
|
||||
case EPD_CMD_MODE:
|
||||
EPD_2IN9_Clear();
|
||||
break;
|
||||
|
||||
case EPD_CMD_RST: // reset
|
||||
// wakeup EPD
|
||||
EPD_SSD_Reset();
|
||||
// soft reset
|
||||
EPD_2IN9_SoftReset();
|
||||
// ready BWR
|
||||
EPD_2IN9_BWR(EPD_WIDTH, EPD_HEIGHT, 0, 0);
|
||||
//EPD_2IN9_Lut(lut_lite_gray8_bwr);
|
||||
break;
|
||||
|
||||
case EPD_CMD_BW: // write BW ram
|
||||
EPD_2IN9_WriteRam(epd_buffer, EPD_WIDTH, EPD_HEIGHT, 0, 0, 0);
|
||||
break;
|
||||
|
||||
case EPD_CMD_RED: // write Red ram
|
||||
EPD_2IN9_WriteRam(epd_buffer, EPD_WIDTH, EPD_HEIGHT, 0, 0, 1);
|
||||
break;
|
||||
|
||||
case EPD_CMD_FILL: { // write ram with color
|
||||
uint8_t color = epd_step_data[0];
|
||||
if (color == 1) { // red
|
||||
memset(epd_buffer, 0xff, EPD_WIDTH*EPD_HEIGHT/8);
|
||||
EPD_2IN9_WriteRam(epd_buffer, EPD_WIDTH, EPD_HEIGHT, 0, 0, 1);
|
||||
} else {
|
||||
memset(epd_buffer, 0, EPD_WIDTH*EPD_HEIGHT/8);
|
||||
EPD_2IN9_WriteRam(epd_buffer, EPD_WIDTH, EPD_HEIGHT, 0, 0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EPD_CMD_DP: { // master
|
||||
// display lut select
|
||||
uint8_t dpm = epd_step_data[0];
|
||||
if (dpm == 1) { // lut1
|
||||
EPD_2IN9_Lut_ById(0);
|
||||
} else if (dpm == 2) { // lut2
|
||||
EPD_2IN9_Lut_ById(1);
|
||||
} else if (dpm == 3) { // lut3
|
||||
EPD_2IN9_Lut_ById(2);
|
||||
} else if (dpm == 0xff) { // user ble lut
|
||||
EPD_2IN9_Lut(ble_data);
|
||||
}
|
||||
|
||||
// otherwise using full lut.
|
||||
EPD_2IN9_Display(dpm ? 0xc7: 0xf7); // fast display
|
||||
EPD_SSD_WaitBusy(15 * 1000);
|
||||
EPD_2IN9_Sleep();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
if (step == epd_step) {
|
||||
epd_step = EPD_CMD_NC;
|
||||
}
|
||||
}
|
||||
|
||||
int EPD_SSD_Update(void)
|
||||
{
|
||||
if (epd_mode == EPD_MODE_IMG) {
|
||||
EPD_2IN9_Update_Image();
|
||||
// stop tick clock
|
||||
return 0;
|
||||
}
|
||||
// default mode
|
||||
EPD_2IN9_Update_Clock();
|
||||
// need tick clock
|
||||
return 1;
|
||||
}
|
||||
|
||||
void EPD_SSD_Init(void)
|
||||
{
|
||||
EPD_SSD_Reset();
|
||||
|
||||
// if the rtc ahead 10 seconds per day (24 hours)
|
||||
// ICALL still using 0x8000 (32768 ticks) for 1 seconds,
|
||||
// (0x8000 + x) / 0x8000 = (24 * 3600) / (24 * 3600 - 10)
|
||||
// 32768 * 10 / (24 * 3600) = 3.793
|
||||
RTC_SetCollaborate(epd_rtc_collab);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
#include <ti/sysbios/knl/Task.h> // Task_sleep
|
||||
#include <driverlib/cpu.h> // CPUDelay
|
||||
|
||||
#include <osal.h>
|
||||
#include <osal_snv.h> // osal_snv_write
|
||||
|
||||
#include "epd_driver.h"
|
||||
#include "task_epd.h"
|
||||
|
||||
// gpio setting
|
||||
static PIN_Handle GPIOHandle = NULL;
|
||||
@@ -29,14 +33,32 @@ uint8_t epd_buffer[EPD_BUF_MAX];
|
||||
// debug only
|
||||
int lut_size;
|
||||
|
||||
// local time to UTC time offset, in minuts.
|
||||
int32_t utc_offset_mins = 8 * 60; // default is UTC+8
|
||||
|
||||
// battery voltage, in frac <3.8>
|
||||
uint16_t epd_battery;
|
||||
uint16_t epd_battery = -1;
|
||||
|
||||
// in degree celcius, read from EPD.
|
||||
int8_t epd_temperature;
|
||||
|
||||
// local time to UTC time offset, in minuts.
|
||||
int32_t utc_offset_mins = 8 * 60; // default is UTC+8
|
||||
// display mode (default is clock)
|
||||
uint8_t epd_mode = EPD_MODE_CLOCK;
|
||||
|
||||
// rtc collaboration, default -3.
|
||||
int8_t epd_rtc_collab = -3;
|
||||
|
||||
// EPD BLE step
|
||||
uint8_t epd_step = EPD_CMD_NC;
|
||||
uint8_t epd_step_data[EPD_STEP_DATA_LEN]; // store parameters
|
||||
|
||||
// the clock refesh on change
|
||||
uint8_t clock_last = 0;
|
||||
|
||||
// BLE data buffer
|
||||
uint8_t ble_data[BLE_DATA_MAX];
|
||||
uint8_t ble_data_len = 0;
|
||||
uint8_t ble_data_cur = 0;
|
||||
|
||||
/*
|
||||
* Device APIs
|
||||
@@ -226,6 +248,39 @@ uint8_t EPD_BATT_Percent(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RTC minor adjustment support.
|
||||
*/
|
||||
#if 1
|
||||
#include <driverlib/aon_wuc.h>
|
||||
#include <driverlib/../inc/hw_aux_wuc.h>
|
||||
|
||||
// collaborate rtc tick, slow down(<0), speed up(>0)
|
||||
void RTC_SetCollaborate( int8_t rtc_collab )
|
||||
{
|
||||
uint32_t subSecInc = (0x8000 + (int)rtc_collab) << 8;
|
||||
// Loading a new RTCSUBSECINC value is done in 5 steps:
|
||||
// 1. Write bit[15:0] of new SUBSECINC value to AUX_WUC_O_RTCSUBSECINC0
|
||||
// 2. Write bit[23:16] of new SUBSECINC value to AUX_WUC_O_RTCSUBSECINC1
|
||||
// 3. Set AUX_WUC_RTCSUBSECINCCTL_UPD_REQ
|
||||
// 4. Wait for AUX_WUC_RTCSUBSECINCCTL_UPD_ACK
|
||||
// 5. Clear AUX_WUC_RTCSUBSECINCCTL_UPD_REQ
|
||||
HWREG( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINC0 ) = (( subSecInc ) & 0xFFFF );
|
||||
HWREG( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINC1 ) = (( subSecInc >> 16 ) & 0xFF );
|
||||
|
||||
HWREG( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINCCTL ) = 1;
|
||||
while( ! ( HWREGBITW( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINCCTL, AUX_WUC_RTCSUBSECINCCTL_UPD_ACK_BITN )));
|
||||
HWREG( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINCCTL ) = 0;
|
||||
|
||||
// save
|
||||
epd_rtc_collab = rtc_collab;
|
||||
}
|
||||
|
||||
int8_t RTC_GetCollaborate( void )
|
||||
{
|
||||
return epd_rtc_collab;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Select a EPD
|
||||
#if defined(EPD_2IN13_SSD1680)
|
||||
|
||||
@@ -241,52 +296,203 @@ uint8_t EPD_BATT_Percent(void)
|
||||
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#include <driverlib/aon_wuc.h>
|
||||
#include <driverlib/../inc/hw_aux_wuc.h>
|
||||
|
||||
// collaborate rtc tick, slow down(<0), speed up(>0)
|
||||
void RTC_Collaborate( int rtc_collab )
|
||||
// do command from BLE
|
||||
void EPD_Command(const uint8_t *cmd, int cmd_len)
|
||||
{
|
||||
uint32_t subSecInc = (0x8000 + rtc_collab) << 8;
|
||||
// Loading a new RTCSUBSECINC value is done in 5 steps:
|
||||
// 1. Write bit[15:0] of new SUBSECINC value to AUX_WUC_O_RTCSUBSECINC0
|
||||
// 2. Write bit[23:16] of new SUBSECINC value to AUX_WUC_O_RTCSUBSECINC1
|
||||
// 3. Set AUX_WUC_RTCSUBSECINCCTL_UPD_REQ
|
||||
// 4. Wait for AUX_WUC_RTCSUBSECINCCTL_UPD_ACK
|
||||
// 5. Clear AUX_WUC_RTCSUBSECINCCTL_UPD_REQ
|
||||
HWREG( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINC0 ) = (( subSecInc ) & 0xFFFF );
|
||||
HWREG( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINC1 ) = (( subSecInc >> 16 ) & 0xFF );
|
||||
// buffer recv position (max 64k)
|
||||
static uint16_t buf_cur = 0;
|
||||
bool need_update = 0;
|
||||
|
||||
HWREG( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINCCTL ) = 1;
|
||||
while( ! ( HWREGBITW( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINCCTL, AUX_WUC_RTCSUBSECINCCTL_UPD_ACK_BITN )));
|
||||
HWREG( AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINCCTL ) = 0;
|
||||
switch (cmd[0]) {
|
||||
// clear screen
|
||||
case EPD_CMD_CLR:
|
||||
need_update = 1;
|
||||
break;
|
||||
|
||||
// display mode change
|
||||
case EPD_CMD_MODE:
|
||||
if (epd_mode != cmd[1]) {
|
||||
clock_last = 0; // stop clock
|
||||
epd_mode = cmd[1];
|
||||
need_update = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
// recv epd_buffer
|
||||
case EPD_CMD_BUF:
|
||||
buf_cur = 0;
|
||||
// pass through
|
||||
case EPD_CMD_BUF_CONT: {
|
||||
uint16_t len = cmd_len - 1;
|
||||
len = MIN(len, (EPD_BUF_MAX - buf_cur));
|
||||
memcpy(&epd_buffer[buf_cur], &cmd[1], len);
|
||||
buf_cur += len;
|
||||
break;
|
||||
}
|
||||
|
||||
// recv LUT
|
||||
case EPD_CMD_LUT:
|
||||
// write LUT to ble_data
|
||||
ble_data_len = cmd_len - 1;
|
||||
memcpy(ble_data, &cmd[1], ble_data_len);
|
||||
break;
|
||||
|
||||
// EPD Reset
|
||||
case EPD_CMD_RST:
|
||||
need_update = 1;
|
||||
break;
|
||||
|
||||
// write BW ram
|
||||
case EPD_CMD_BW:
|
||||
need_update = 1;
|
||||
break;
|
||||
|
||||
// write RED ram
|
||||
case EPD_CMD_RED:
|
||||
need_update = 1;
|
||||
break;
|
||||
|
||||
// Display
|
||||
case EPD_CMD_DP:
|
||||
epd_step_data[0] = cmd[1];
|
||||
need_update = 1;
|
||||
break;
|
||||
|
||||
// fill ram with color
|
||||
case EPD_CMD_FILL:
|
||||
epd_step_data[0] = cmd[1];
|
||||
need_update = 1;
|
||||
break;
|
||||
|
||||
// put data to buffer
|
||||
case EPD_CMD_BUF_PUT: {
|
||||
// cmd, idx, data ...
|
||||
uint8_t idx = cmd[1];
|
||||
uint8_t len = MIN(cmd_len - 2, BLE_DATA_MAX - idx);
|
||||
// calculate data length
|
||||
ble_data_len = idx + len;
|
||||
// save data
|
||||
memcpy(&ble_data[idx], &cmd[2], len);
|
||||
break;
|
||||
}
|
||||
|
||||
// get data from buffer, data send by ReadAttr
|
||||
case EPD_CMD_BUF_GET: {
|
||||
// cmd, idx
|
||||
ble_data_cur = cmd[1];
|
||||
break;
|
||||
}
|
||||
|
||||
// write ble_data to snv
|
||||
case EPD_CMD_SNV_WRITE: {
|
||||
uint8_t id = cmd[1];
|
||||
if (ble_data_len != 0) {
|
||||
osal_snv_write(id, ble_data_len, ble_data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// read snv to ble_data
|
||||
case EPD_CMD_SNV_READ: {
|
||||
// cmd, id, len
|
||||
uint8_t id = cmd[1];
|
||||
uint8_t len = cmd[2];
|
||||
uint8_t rc = osal_snv_read(id, len, ble_data);
|
||||
ble_data_len = (rc == SUCCESS) ? len : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// save configuration to snv
|
||||
case EPD_CMD_SAVE_CFG:
|
||||
EPD_SNV_SaveCfg();
|
||||
break;
|
||||
|
||||
default:
|
||||
// ignore the command.
|
||||
return;
|
||||
}
|
||||
|
||||
// save step
|
||||
epd_step = cmd[0];
|
||||
|
||||
// notifiy EPDTask if needs
|
||||
if (need_update) {
|
||||
EPDTask_Update();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
// get EPD state
|
||||
int EPD_State(uint8_t *buf, uint8_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// load configuration from SNV
|
||||
int EPD_SNV_LoadCfg()
|
||||
{
|
||||
struct epd_snv_cfg cfg;
|
||||
uint8_t rc = osal_snv_read(EPD_SNV_CFG, sizeof(cfg), &cfg);
|
||||
if (rc == SUCCESS) {
|
||||
epd_mode = cfg.u.cfg.mode;
|
||||
utc_offset_mins = cfg.u.cfg.utc_offset;
|
||||
epd_rtc_collab = cfg.u.cfg.rtc_collab;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
// save configuration to SNV
|
||||
int EPD_SNV_SaveCfg()
|
||||
{
|
||||
struct epd_snv_cfg cfg;
|
||||
cfg.u.cfg.mode = epd_mode;
|
||||
cfg.u.cfg.utc_offset = utc_offset_mins;
|
||||
cfg.u.cfg.rtc_collab = epd_rtc_collab;
|
||||
return osal_snv_write(EPD_SNV_CFG, sizeof(cfg), &cfg);
|
||||
}
|
||||
|
||||
// load lut from SNV
|
||||
int EPD_SNV_LoadLut(int index, uint8_t *lut, int len)
|
||||
{
|
||||
uint8_t rc;
|
||||
return osal_snv_read(EPD_SNV_LUT1 + index, len, lut);
|
||||
}
|
||||
|
||||
// save lut to SNV
|
||||
int EPD_SNV_SaveLut(int index, const uint8_t *lut, int len)
|
||||
{
|
||||
uint8_t rc;
|
||||
return osal_snv_write(EPD_SNV_LUT1 + index, len, (void *)lut);
|
||||
}
|
||||
|
||||
// should be only called once!
|
||||
void EPD_Init()
|
||||
{
|
||||
GPIOHandle = PIN_open(&GPIOState, GPIOTable);
|
||||
|
||||
// test LUT size
|
||||
// test LUT size, different EPD has different LUT size.
|
||||
//lut_size = EPD_LUT_Detect();
|
||||
|
||||
// if the rtc ahead 10 seconds per day (24 hours)
|
||||
// ICALL still using 0x8000 (32768 ticks) for 1 seconds,
|
||||
// (0x8000 + x) / 0x8000 = (24 * 3600) / (24 * 3600 - 10)
|
||||
// 32768 * 10 / (24 * 3600) = 3.793
|
||||
//RTC_Collaborate(-3);
|
||||
#if 0
|
||||
uint8_t buf[16] = {'a', 'b', 'c', 'd', };
|
||||
uint8_t rc = osal_snv_read(0x80, 16, buf);
|
||||
if (rc != SUCCESS) {
|
||||
memcpy(buf, "hello world.", 13);
|
||||
osal_snv_write(0x80, 16, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
// load from snv
|
||||
EPD_SNV_LoadCfg();
|
||||
|
||||
// init EPD
|
||||
EPD_SSD_Init();
|
||||
}
|
||||
|
||||
void EPD_Update()
|
||||
int EPD_Update()
|
||||
{
|
||||
// update battery level
|
||||
epd_battery = AONBatMonBatteryVoltageGet();
|
||||
epd_battery = MIN(epd_battery, AONBatMonBatteryVoltageGet());
|
||||
|
||||
// update Display
|
||||
EPD_SSD_Update();
|
||||
return EPD_SSD_Update();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _EPD_DRIVER_H_
|
||||
#define _EPD_DRIVER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdint.h> // uint8_t
|
||||
|
||||
// select a EPD device
|
||||
//#define EPD_2IN13_SSD1680
|
||||
@@ -24,7 +24,75 @@ extern int32_t utc_offset_mins;
|
||||
|
||||
// public sensor values
|
||||
extern int8_t epd_temperature; // in degree celsius, +/-127
|
||||
extern uint16_t epd_battery; // in (3.8) frac
|
||||
extern uint16_t epd_battery; // in (3.8) frac, minium value
|
||||
|
||||
// display mode
|
||||
extern uint8_t epd_mode;
|
||||
#define EPD_MODE_CLOCK 0 // realtime clock
|
||||
#define EPD_MODE_IMG 1 // static image
|
||||
|
||||
// TBD: split image display to steps in a dirty way.
|
||||
extern uint8_t epd_step;
|
||||
#define EPD_STEP_DATA_LEN 4
|
||||
extern uint8_t epd_step_data[EPD_STEP_DATA_LEN];
|
||||
|
||||
// saved clock tick.
|
||||
extern uint8_t clock_last;
|
||||
|
||||
// ble data buffer
|
||||
#define BLE_DATA_MAX 256
|
||||
extern uint8_t ble_data[BLE_DATA_MAX]; // ble data, a 256 bytes buffer
|
||||
extern uint8_t ble_data_len; // indicate the length of ble_data
|
||||
extern uint8_t ble_data_cur; // indicate the current position of ble_data
|
||||
|
||||
// EPD service commands
|
||||
enum EPD_CMD {
|
||||
EPD_CMD_NC = 0, // nothing
|
||||
|
||||
// keep order
|
||||
EPD_CMD_CLR, // clear screen
|
||||
EPD_CMD_MODE, // set display mode
|
||||
EPD_CMD_BUF, // first receive to epd_buffer
|
||||
EPD_CMD_BUF_CONT, // continue write to epd_buffer
|
||||
EPD_CMD_LUT, // set lut
|
||||
EPD_CMD_RST, // reset EPD
|
||||
EPD_CMD_BW, // write EPD Black/White
|
||||
EPD_CMD_RED, // write EPD Red
|
||||
EPD_CMD_DP, // EPD display
|
||||
EPD_CMD_FILL, // fill ram with color
|
||||
|
||||
EPD_CMD_BUF_PUT, // put data to buffer
|
||||
EPD_CMD_BUF_GET, // get data from buffer
|
||||
EPD_CMD_SNV_WRITE, // write ble_data to snv
|
||||
EPD_CMD_SNV_READ, // read snv to ble_data
|
||||
|
||||
EPD_CMD_SAVE_CFG, // save configuration to snv, rtc collaborate, utc offset, etc ...
|
||||
|
||||
EPD_CMD_MAX
|
||||
};
|
||||
|
||||
// CC2640r2 SNV user area, 0x80 - 0x8f
|
||||
enum EPD_SNV {
|
||||
EPD_SNV_CFG = 0x80, // configuration
|
||||
EPD_SNV_LUT1, // LUT1, fast bw
|
||||
EPD_SNV_LUT2, // LUT2, gray bwr
|
||||
EPD_SNV_LUT3, // LUT3
|
||||
|
||||
EPD_SNV_MAX
|
||||
};
|
||||
|
||||
// EPD configuration in SNV
|
||||
struct epd_snv_cfg {
|
||||
union {
|
||||
struct {
|
||||
uint8_t mode; // saved display mode
|
||||
int8_t rtc_collab; // saved rtc collaborate
|
||||
int16_t utc_offset; // saved utc offset in minutes
|
||||
} cfg;
|
||||
uint8_t raw[32];
|
||||
} u;
|
||||
};
|
||||
|
||||
/*
|
||||
* <int.frac> format size (3.8) bits.
|
||||
* int for 0-3 voltage
|
||||
@@ -34,9 +102,17 @@ extern uint16_t epd_battery; // in (3.8) frac
|
||||
#define INTFRAC_mV(x) ((x&0xff)*125/32)
|
||||
#define INTFRAC2MV(x) (INTFRAC_mV(x)+(INTFRAC_V(x)*1000))
|
||||
|
||||
// Macro
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? a : b)
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a) > (b) ? a : b)
|
||||
#endif
|
||||
|
||||
// the driver public,
|
||||
void EPD_Init();
|
||||
void EPD_Update();
|
||||
int EPD_Update();
|
||||
void EPD_SSD_Reset();
|
||||
void EPD_SSD_SendCommand(uint8_t reg);
|
||||
void EPD_SSD_SendData(uint8_t data);
|
||||
@@ -46,8 +122,21 @@ bool EPD_SSD_IsBusy();
|
||||
void EPD_SSD_WaitBusy(uint32_t ms);
|
||||
uint8_t EPD_BATT_Percent();
|
||||
|
||||
// RTC
|
||||
void RTC_SetCollaborate( int8_t rtc_collab );
|
||||
int8_t RTC_GetCollaborate();
|
||||
|
||||
// SNV
|
||||
int EPD_SNV_LoadCfg();
|
||||
int EPD_SNV_SaveCfg();
|
||||
int EPD_SNV_LoadLut(int index, uint8_t *lut, int len);
|
||||
int EPD_SNV_SaveLut(int index, const uint8_t *lut, int len);
|
||||
|
||||
// API for EPD commands
|
||||
void EPD_Command(const uint8_t *cmd, int cmd_len);
|
||||
|
||||
// for epd_inch.c shoule implement,
|
||||
void EPD_SSD_Init();
|
||||
void EPD_SSD_Upate();
|
||||
int EPD_SSD_Upate();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "epd_driver.h" // epd_battery, epd_temperature
|
||||
#include "epd_service.h"
|
||||
#include "task_epd.h" // EPDTask_Update
|
||||
|
||||
// EPD_Service Service UUID
|
||||
CONST uint8 EpdServiceUUID[ATT_BT_UUID_SIZE] = {
|
||||
@@ -40,29 +41,39 @@ CONST uint8 EpdRtcCollabUUID[ATT_BT_UUID_SIZE] = {
|
||||
LO_UINT16(EPD_RTC_COLLAB_UUID), HI_UINT16(EPD_RTC_COLLAB_UUID),
|
||||
};
|
||||
|
||||
CONST uint8 EpdRxTxUUID[ATT_BT_UUID_SIZE] = {
|
||||
LO_UINT16(EPD_RXTX_UUID), HI_UINT16(EPD_RXTX_UUID),
|
||||
};
|
||||
|
||||
static EpdServiceCBs_t *pAppCBs = NULL;
|
||||
static gattCharCfg_t *EpdDataConfig;
|
||||
|
||||
// Service declaration
|
||||
static CONST gattAttrType_t EpdServiceDecl = { ATT_BT_UUID_SIZE, EpdServiceUUID };
|
||||
static uint8 EpochProps = GATT_PROP_READ | GATT_PROP_WRITE;
|
||||
static uint8 EpochVal[4] = {0};
|
||||
|
||||
//static uint8 EpochDesc[] = "Unix Epoch";
|
||||
static uint8 EpochProps = GATT_PROP_READ | GATT_PROP_WRITE;
|
||||
static uint32 EpochLastVal = 0; // last value of Unix Epoch
|
||||
|
||||
static uint8 UtcOffProps = GATT_PROP_READ | GATT_PROP_WRITE;
|
||||
//static uint8 UtcOffDesc[] = "UTC Offset Mins";
|
||||
static int8 UtcOffVal[4] = {0};
|
||||
static uint8 UtcOffProps = GATT_PROP_READ | GATT_PROP_WRITE;
|
||||
//static int8 UtcOffVal[4] = {0};
|
||||
|
||||
static uint8 BattProps = GATT_PROP_READ;
|
||||
//static uint8 BattDesc[] = "Battery mv";
|
||||
static uint8 BattVal[2] = {0};
|
||||
static uint8 BattProps = GATT_PROP_READ;
|
||||
//static uint8 BattVal[2] = {0};
|
||||
|
||||
static uint8 TempProps = GATT_PROP_READ;
|
||||
//static uint8 TempDesc[] = "Temperature";
|
||||
static int8 TempVal[1] = {0};
|
||||
static uint8 TempProps = GATT_PROP_READ;
|
||||
//static int8 TempVal[1] = {0};
|
||||
|
||||
// RTC Collaboration
|
||||
static uint8 RtcCollabProps = GATT_PROP_READ | GATT_PROP_WRITE;
|
||||
static int8 RtcCollabVal[1] = {0};
|
||||
//static int8 RtcCollabVal[1] = {0};
|
||||
|
||||
// RxTx service
|
||||
static uint8 RxTxProps = GATT_PROP_READ | GATT_PROP_WRITE;
|
||||
//static uint8 RxTxBuf[64];
|
||||
|
||||
static gattAttribute_t EpdServiceAttrTbl[] =
|
||||
{
|
||||
@@ -86,7 +97,7 @@ static gattAttribute_t EpdServiceAttrTbl[] =
|
||||
{ ATT_BT_UUID_SIZE, EpdEpochUUID },
|
||||
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
|
||||
0,
|
||||
EpochVal
|
||||
NULL
|
||||
},
|
||||
|
||||
// Characteristic Declaration
|
||||
@@ -101,7 +112,7 @@ static gattAttribute_t EpdServiceAttrTbl[] =
|
||||
{ ATT_BT_UUID_SIZE, EpdUtcOffsetUUID },
|
||||
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
|
||||
0,
|
||||
UtcOffVal
|
||||
NULL
|
||||
},
|
||||
|
||||
// Characteristic Declaration
|
||||
@@ -116,7 +127,7 @@ static gattAttribute_t EpdServiceAttrTbl[] =
|
||||
{ ATT_BT_UUID_SIZE, EpdBattUUID },
|
||||
GATT_PERMIT_READ,
|
||||
0,
|
||||
BattVal
|
||||
NULL
|
||||
},
|
||||
|
||||
// Characteristic Declaration
|
||||
@@ -131,7 +142,7 @@ static gattAttribute_t EpdServiceAttrTbl[] =
|
||||
{ ATT_BT_UUID_SIZE, EpdTempUUID },
|
||||
GATT_PERMIT_READ,
|
||||
0,
|
||||
TempVal
|
||||
NULL
|
||||
},
|
||||
|
||||
// Characteristic Declaration
|
||||
@@ -146,7 +157,22 @@ static gattAttribute_t EpdServiceAttrTbl[] =
|
||||
{ ATT_BT_UUID_SIZE, EpdRtcCollabUUID },
|
||||
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
|
||||
0,
|
||||
RtcCollabVal
|
||||
NULL
|
||||
},
|
||||
|
||||
// Characteristic Declaration
|
||||
{
|
||||
{ ATT_BT_UUID_SIZE, characterUUID },
|
||||
GATT_PERMIT_READ,
|
||||
0,
|
||||
&RxTxProps
|
||||
},
|
||||
// Characteristic Value
|
||||
{
|
||||
{ ATT_BT_UUID_SIZE, EpdRxTxUUID },
|
||||
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
};
|
||||
|
||||
@@ -231,12 +257,7 @@ bStatus_t EPDService_GetParameter(uint8_t param, uint16_t *len, void *value)
|
||||
{
|
||||
bStatus_t ret = SUCCESS;
|
||||
switch (param) {
|
||||
#if 0
|
||||
case EPD_EPOCH_ID:
|
||||
*len = 4;
|
||||
memcpy(value, EpochVal, *len);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = INVALIDPARAMETER;
|
||||
break;
|
||||
@@ -244,33 +265,6 @@ bStatus_t EPDService_GetParameter(uint8_t param, uint16_t *len, void *value)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static uint8_t EPDService_findCharParamId(gattAttribute_t *pAttr)
|
||||
{
|
||||
#if 0
|
||||
// Is this a Client Characteristic Configuration Descriptor?
|
||||
if(ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID ==
|
||||
*(uint16_t *)pAttr->type.uuid) {
|
||||
return (EPDService_findCharParamId(pAttr - 1)); // Assume the value attribute precedes CCCD and recurse
|
||||
} elif
|
||||
#endif
|
||||
if (ATT_BT_UUID_SIZE == pAttr->type.len) {
|
||||
uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]);
|
||||
switch (uuid) {
|
||||
case EPD_EPOCH_UUID:
|
||||
return EPD_EPOCH_ID;
|
||||
case EPD_UTC_OFFSET_UUID:
|
||||
return EPD_UTC_OFFSET_ID;
|
||||
case EPD_BATT_UUID:
|
||||
return EPD_BATT_ID;
|
||||
case EPD_TEMP_UUID:
|
||||
return EPD_TEMP_ID;
|
||||
}
|
||||
}
|
||||
return 0xFF; // Not found. Return invalid.
|
||||
}
|
||||
#endif
|
||||
|
||||
static bStatus_t utilExtractUuid16(gattAttribute_t *pAttr, uint16_t *pUuid)
|
||||
{
|
||||
bStatus_t status = SUCCESS;
|
||||
@@ -336,12 +330,23 @@ static bStatus_t EPDService_ReadAttrCB(uint16_t connHandle,
|
||||
}
|
||||
|
||||
case EPD_RTC_COLLAB_UUID: {
|
||||
int8_t v = RtcCollabVal[0];
|
||||
int8_t v = RTC_GetCollaborate();
|
||||
*pLen = sizeof(v);
|
||||
memcpy(pValue, &v, *pLen);
|
||||
break;
|
||||
}
|
||||
|
||||
case EPD_RXTX_UUID: {
|
||||
// send ble_data to BLE.
|
||||
uint8_t idx = ble_data_cur;
|
||||
uint8_t len = MIN(ble_data_len - idx, maxLen - 1);
|
||||
// index, data ...
|
||||
pValue[0] = idx;
|
||||
memcpy(&pValue[1], &ble_data[idx], len);
|
||||
*pLen = len + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return ATT_ERR_ATTR_NOT_FOUND;
|
||||
}
|
||||
@@ -369,6 +374,11 @@ static bStatus_t EPDService_WriteAttrCB(uint16_t connHandle,
|
||||
if (len == 4) {
|
||||
uint32_t t = *(uint32_t*)pValue;
|
||||
Seconds_set(t);
|
||||
EpochLastVal = t;
|
||||
|
||||
// notify EPD to refresh
|
||||
clock_last = 0;
|
||||
EPDTask_Update();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -386,12 +396,18 @@ static bStatus_t EPDService_WriteAttrCB(uint16_t connHandle,
|
||||
case EPD_RTC_COLLAB_UUID: {
|
||||
if (len == 1) {
|
||||
int8_t v = *(int8_t*)pValue;
|
||||
RTC_Collaborate(v);
|
||||
RtcCollabVal[0] = v;
|
||||
RTC_SetCollaborate(v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case EPD_RXTX_UUID: {
|
||||
if (len < 64) {
|
||||
EPD_Command(pValue, len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GATT_CLIENT_CHAR_CFG_UUID:
|
||||
status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY);
|
||||
break;
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
#define EPD_TEMP_UUID 0xFFF4
|
||||
|
||||
// RTC collaboration value
|
||||
#define EPD_RTC_COLLAB_UUID 0xFFF5
|
||||
#define EPD_RTC_COLLAB_UUID 0xFFF5
|
||||
|
||||
// EPD RXTX service
|
||||
#define EPD_RXTX_UUID 0xFFFE
|
||||
|
||||
// Callback when a characteristic value has changed
|
||||
typedef void (*EpdServiceChange_t)(uint16_t connHandle, uint8_t paramID,
|
||||
|
||||
@@ -30,23 +30,24 @@
|
||||
#include "task_ble.h"
|
||||
|
||||
// Advertising interval when device is discoverable (units of 625us, 160=100ms)
|
||||
#define DEFAULT_ADVERTISING_INTERVAL 160
|
||||
#define DEFAULT_ADVERTISING_INTERVAL 1600
|
||||
|
||||
// General discoverable mode: advertise indefinitely
|
||||
#define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL
|
||||
|
||||
// Minimum connection interval (units of 1.25ms, 80=100ms) for automatic
|
||||
// Minimum connection interval (units of 1.25ms, 80=100ms, min=6) for automatic
|
||||
// parameter update request
|
||||
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 80
|
||||
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 16
|
||||
|
||||
// Maximum connection interval (units of 1.25ms, 800=1000ms) for automatic
|
||||
// Maximum connection interval (units of 1.25ms, 800=1000ms, max=3200) for automatic
|
||||
// parameter update request
|
||||
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 800
|
||||
|
||||
// Slave latency to use for automatic parameter update request
|
||||
#define DEFAULT_DESIRED_SLAVE_LATENCY 0
|
||||
#define DEFAULT_DESIRED_SLAVE_LATENCY 3
|
||||
|
||||
// Supervision timeout value (units of 10ms, 1000=10s) for automatic parameter
|
||||
// MEETS : CONN_TIMEOUT > (1 + SLAVE_LATENCY) * CONN_INTERVAL
|
||||
// Supervision timeout value (units of 10ms, 1000=10s, 10-3200) for automatic parameter
|
||||
// update request
|
||||
#define DEFAULT_DESIRED_CONN_TIMEOUT 1000
|
||||
|
||||
@@ -55,10 +56,10 @@
|
||||
#define DEFAULT_ENABLE_UPDATE_REQUEST GAPROLE_LINK_PARAM_UPDATE_WAIT_REMOTE_PARAMS
|
||||
|
||||
// Connection Pause Peripheral time value (in seconds)
|
||||
#define DEFAULT_CONN_PAUSE_PERIPHERAL 6
|
||||
#define DEFAULT_CONN_PAUSE_PERIPHERAL 5
|
||||
|
||||
// How often to perform periodic event (in msec)
|
||||
#define SBP_PERIODIC_EVT_PERIOD 5000
|
||||
//#define SBP_PERIODIC_EVT_PERIOD 5000
|
||||
|
||||
// Application specific event ID for HCI Connection Event End Events
|
||||
#define SBP_HCI_CONN_EVT_END_EVT 0x0001
|
||||
@@ -78,11 +79,13 @@
|
||||
// Internal Events for RTOS application
|
||||
#define SBP_ICALL_EVT ICALL_MSG_EVENT_ID // Event_Id_31
|
||||
#define SBP_QUEUE_EVT UTIL_QUEUE_EVENT_ID // Event_Id_30
|
||||
#define SBP_RXTX_QUEUE_EVT Event_Id_01
|
||||
//#define SBP_PERIODIC_EVT Event_Id_00
|
||||
|
||||
// Bitwise OR of all events to pend on
|
||||
#define SBP_ALL_EVENTS (SBP_ICALL_EVT | \
|
||||
SBP_QUEUE_EVT)
|
||||
SBP_QUEUE_EVT | \
|
||||
SBP_RXTX_QUEUE_EVT)
|
||||
|
||||
// Row numbers for two-button menu
|
||||
#define SBP_ROW_RESULT TBM_ROW_APP
|
||||
@@ -160,18 +163,12 @@ static uint8_t advertData[] =
|
||||
GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all
|
||||
LO_UINT16(EPD_SERVICE_SERV_UUID),
|
||||
HI_UINT16(EPD_SERVICE_SERV_UUID)
|
||||
|
||||
#else
|
||||
16, 0x16, // spec
|
||||
LO_UINT16(EPD_SERVICE_SERV_UUID), HI_UINT16(EPD_SERVICE_SERV_UUID),
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mac address
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // data
|
||||
0x07,
|
||||
|
||||
0x03, // length of this data
|
||||
GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all
|
||||
LO_UINT16(EPD_SERVICE_SERV_UUID),
|
||||
HI_UINT16(EPD_SERVICE_SERV_UUID)
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -533,6 +530,11 @@ static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1)
|
||||
}
|
||||
}
|
||||
|
||||
// RXTX events
|
||||
if (events & SBP_RXTX_QUEUE_EVT) {
|
||||
|
||||
}
|
||||
|
||||
// If RTOS queue is not empty, process app message.
|
||||
if (events & SBP_QUEUE_EVT) {
|
||||
while (!Queue_empty(appMsgQueue)) {
|
||||
@@ -803,10 +805,10 @@ static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState)
|
||||
case GAPROLE_STARTED:
|
||||
{
|
||||
uint8_t ownAddress[B_ADDR_LEN];
|
||||
uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
|
||||
|
||||
GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);
|
||||
|
||||
#if 0
|
||||
uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
|
||||
// use 6 bytes of device address for 8 bytes of system ID value
|
||||
systemId[0] = ownAddress[0];
|
||||
systemId[1] = ownAddress[1];
|
||||
@@ -826,6 +828,7 @@ static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState)
|
||||
// Display device address
|
||||
Display_print0(dispHandle, SBP_ROW_BDADDR, 0, Util_convertBdAddr2Str(ownAddress));
|
||||
Display_print0(dispHandle, SBP_ROW_ROLESTATE, 0, "Initialized");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ void TaskEPD_createTask(void)
|
||||
Task_construct(&EPDTask, TaskEPD_taskFxn, &taskParams, NULL);
|
||||
}
|
||||
|
||||
void EPDTask_Update(void)
|
||||
{
|
||||
Event_post(syncEvent, EPDTASK_EVENT_PERIODIC);
|
||||
}
|
||||
|
||||
static void EPDTask_clockHandler(UArg arg)
|
||||
{
|
||||
Event_post(syncEvent, arg);
|
||||
@@ -72,9 +77,14 @@ void TaskEPD_taskInit(void)
|
||||
// so that the application can send and receive messages.
|
||||
ICall_registerApp(&selfEntity, &syncEvent);
|
||||
|
||||
// second period timer
|
||||
Util_constructClock(&periodicClock, EPDTask_clockHandler,
|
||||
1000, 0, false, EPDTASK_EVENT_PERIODIC);
|
||||
|
||||
// init EPD
|
||||
EPD_Init();
|
||||
|
||||
// start timer
|
||||
Util_startClock(&periodicClock);
|
||||
}
|
||||
|
||||
@@ -88,10 +98,9 @@ void TaskEPD_taskFxn(UArg a0, UArg a1)
|
||||
events = Event_pend(syncEvent, Event_Id_NONE, EPDTASK_EVENT_ALL, ICALL_TIMEOUT_FOREVER);
|
||||
|
||||
if (events & EPDTASK_EVENT_PERIODIC) {
|
||||
|
||||
EPD_Update();
|
||||
|
||||
Util_startClock(&periodicClock);
|
||||
if (EPD_Update()) {
|
||||
Util_startClock(&periodicClock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,7 @@ typedef void (*EpdResponseCallback)(uint8_t event, uint8_t *buf, uint8_t len);
|
||||
void EPDTask_RegisterResponseCallback(EpdResponseCallback callback);
|
||||
void TaskEPD_createTask(void);
|
||||
|
||||
// api for event_post
|
||||
void EPDTask_Update(void);
|
||||
|
||||
#endif
|
||||
@@ -32,7 +32,7 @@
|
||||
--cmd_file=${SRC_BLE_DIR}/config/build_components.opt
|
||||
--cmd_file=${SRC_BLE_DIR}/config/factory_config.opt
|
||||
--cmd_file=${WORKSPACE_LOC}/ble5_simple_peripheral_cc2640r2lp_stack_library/TOOLS/build_config.opt
|
||||
-mv7M3 -O4 --opt_for_speed=0 --code_state=16 --abi=eabi -me -g --c99 --gcc --gen_func_subsections=on --display_error_number --diag_wrap=off
|
||||
-mv7M3 -O2 --opt_for_speed=0 --code_state=16 --abi=eabi -me -g --c99 --gcc --gen_func_subsections=on --display_error_number --diag_wrap=off
|
||||
-DDeviceFamily_CC26X0R2
|
||||
-DBOARD_DISPLAY_USE_LCD=0
|
||||
-DBOARD_DISPLAY_USE_UART=0
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
--cmd_file=${SRC_BLE_DIR}/config/build_components.opt
|
||||
--cmd_file=${SRC_BLE_DIR}/config/factory_config.opt
|
||||
--cmd_file=${PROJECT_LOC}/TOOLS/build_config.opt
|
||||
-mv7M3 -O4 --opt_for_speed=0 --code_state=16 --abi=eabi -me -g --c99 --gcc --gen_func_subsections=on --display_error_number --diag_wrap=off
|
||||
-mv7M3 -O2 --opt_for_speed=0 --code_state=16 --abi=eabi -me -g --c99 --gcc --gen_func_subsections=on --display_error_number --diag_wrap=off
|
||||
-D${XDC_SYMBOLS}
|
||||
-DCC26XX
|
||||
-DCC26XX_R2
|
||||
|
||||
10
tools/ble5_ctrl.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import asyncio
|
||||
from bleak import BleakScanner
|
||||
|
||||
async def main():
|
||||
devices = await BleakScanner.discover()
|
||||
for d in devices:
|
||||
print(d, d.metadata)
|
||||
BleakScanner.discovered_devices_and_advertisement_data = True
|
||||
|
||||
asyncio.run(main())
|
||||
BIN
tools/bwr_gray8.act
Normal file
@@ -1,28 +1,42 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<style>
|
||||
div {
|
||||
padding: 12px;
|
||||
margin: 2 auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
let bleDevice;
|
||||
let gattServer;
|
||||
let Theservice;
|
||||
let epdService;
|
||||
let epochCharacter;
|
||||
|
||||
async function sleep(ms) {
|
||||
await new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function doConnect() {
|
||||
if (gattServer != null && gattServer.connected) {
|
||||
if (bleDevice != null && bleDevice.gatt.connected)
|
||||
bleDevice.gatt.disconnect();
|
||||
}
|
||||
else {
|
||||
bleDevice = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ namePrefix: ['C26_'] }],
|
||||
optionalServices: [
|
||||
0xfff0,
|
||||
],
|
||||
//acceptAllDevices: true
|
||||
});
|
||||
await bleDevice.addEventListener('gattserverdisconnected', disconnect);
|
||||
await connect();
|
||||
}
|
||||
bleDevice = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ namePrefix: ['C26_'] }],
|
||||
optionalServices: [
|
||||
0xfff0,
|
||||
],
|
||||
//acceptAllDevices: true
|
||||
});
|
||||
await bleDevice.addEventListener('gattserverdisconnected', disconnect);
|
||||
await connect();
|
||||
}
|
||||
}
|
||||
|
||||
async function connect() {
|
||||
@@ -34,66 +48,257 @@
|
||||
info('> Found EPD service');
|
||||
epochCharacter = await epdService.getCharacteristic(0xfff1);
|
||||
document.getElementById("btnConnect").innerHTML = 'Disconnect';
|
||||
document.getElementById('etagFn').style.visibility='';
|
||||
document.getElementById('etagFn').style.visibility = '';
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
bleDevice = null;
|
||||
epdService = null;
|
||||
epochCharacter = null;
|
||||
info('Disconnected.');
|
||||
|
||||
document.getElementById("btnConnect").innerHTML = 'Connect';
|
||||
document.getElementById('etagFn').style.visibility='hidden';
|
||||
bleDevice = null;
|
||||
epdService = null;
|
||||
epochCharacter = null;
|
||||
info('Disconnected.');
|
||||
|
||||
document.getElementById("btnConnect").innerHTML = 'Connect';
|
||||
document.getElementById('etagFn').style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
async function doSetTime() {
|
||||
var epoch = Date.now() / 1000 | 0;
|
||||
var buf = new ArrayBuffer(4);
|
||||
var arr = new Uint32Array(buf);
|
||||
arr[0] = epoch;
|
||||
await epochCharacter.writeValueWithResponse(arr);
|
||||
info("Write unix epoch: " + epoch);
|
||||
var epoch = Date.now() / 1000 | 0;
|
||||
var buf = new ArrayBuffer(4);
|
||||
var arr = new Uint32Array(buf);
|
||||
arr[0] = epoch;
|
||||
await epochCharacter.writeValueWithResponse(arr);
|
||||
info("Write unix epoch: " + epoch);
|
||||
}
|
||||
|
||||
async function doReadEtag() {
|
||||
var host_epoch = Date.now() / 1000 | 0;
|
||||
var host_epoch = Date.now() / 1000 | 0;
|
||||
|
||||
// read current time
|
||||
var chr = await epdService.getCharacteristic(0xfff1);
|
||||
var epoch = (await chr.readValue()).getUint32(0, 1);
|
||||
// read current time
|
||||
var chr = await epdService.getCharacteristic(0xfff1);
|
||||
var epoch = (await chr.readValue()).getUint32(0, 1);
|
||||
|
||||
// read time zone
|
||||
var chr = await epdService.getCharacteristic(0xfff2);
|
||||
var tz_min = (await chr.readValue()).getInt32(0, 1);
|
||||
info(`# host time: ${host_epoch}, diff (${epoch - host_epoch}) seconds.`);
|
||||
info(`# etag time: ${epoch}, tz: ${tz_min} minutes of UTC.`);
|
||||
|
||||
// battery
|
||||
var chr = await epdService.getCharacteristic(0xfff3);
|
||||
var batt = (await chr.readValue()).getUint16(0, 1);
|
||||
// read time zone
|
||||
var chr = await epdService.getCharacteristic(0xfff2);
|
||||
var tz_min = (await chr.readValue()).getInt32(0, 1);
|
||||
info(`# host time: ${host_epoch}, diff (${epoch - host_epoch}) seconds.`);
|
||||
info(`# etag time: ${epoch}, tz: ${tz_min} minutes of UTC.`);
|
||||
|
||||
// Temperature
|
||||
var chr = await epdService.getCharacteristic(0xfff4);
|
||||
var temp = (await chr.readValue()).getInt8(0, 1);
|
||||
info(`# etag sensor: battery(${batt}mv), temperature(${temp}'C). `);
|
||||
// battery
|
||||
var chr = await epdService.getCharacteristic(0xfff3);
|
||||
var batt = (await chr.readValue()).getUint16(0, 1);
|
||||
|
||||
// RTC Collaborate
|
||||
var chr = await epdService.getCharacteristic(0xfff5);
|
||||
var rtc_collab = (await chr.readValue()).getInt8(0, 1);
|
||||
info(`# rtc collab: ${rtc_collab} every 1 second.`);
|
||||
// Temperature
|
||||
var chr = await epdService.getCharacteristic(0xfff4);
|
||||
var temp = (await chr.readValue()).getInt8(0, 1);
|
||||
info(`# etag sensor: battery(${batt}mv), temperature(${temp}'C). `);
|
||||
|
||||
// RTC Collaborate
|
||||
var chr = await epdService.getCharacteristic(0xfff5);
|
||||
var rtc_collab = (await chr.readValue()).getInt8(0, 1);
|
||||
info(`# rtc collab: ${rtc_collab} every 1 second.`);
|
||||
}
|
||||
|
||||
async function doRtcCollab() {
|
||||
var col = prompt("对 32.768kHz 晶振补偿频漂,走时快补偿负数,走时慢补偿正数。可选范围 (-3 ~ 3)", 0);
|
||||
if (col == null || col < -3 || col > 3) return;
|
||||
var chr = await epdService.getCharacteristic(0xfff5);
|
||||
var buf = new ArrayBuffer(1);
|
||||
var arr = new Int8Array(buf);
|
||||
arr[0] = parseInt(col);
|
||||
await chr.writeValueWithResponse(arr);
|
||||
info(`write RTC collabration: ${col}`);
|
||||
var col = prompt("对 32.768kHz 晶振补偿频漂,走时快补偿负数,走时慢补偿正数。可选范围 (-3 ~ 3)", 0);
|
||||
if (col == null || col < -3 || col > 3) return;
|
||||
var chr = await epdService.getCharacteristic(0xfff5);
|
||||
var buf = new ArrayBuffer(1);
|
||||
var arr = new Int8Array(buf);
|
||||
arr[0] = parseInt(col);
|
||||
await chr.writeValueWithResponse(arr);
|
||||
info(`write RTC collabration: ${col}`);
|
||||
}
|
||||
|
||||
async function doTest() {
|
||||
var chr = await epdService.getCharacteristic(0xfffe);
|
||||
var buf = new ArrayBuffer(62);
|
||||
var arr = new Int8Array(buf);
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = i % 8;
|
||||
}
|
||||
await chr.writeValueWithResponse(arr);
|
||||
info(`> write ${arr.length} bytes.`)
|
||||
//var out = await chr.readValue();
|
||||
//console.log(out);
|
||||
}
|
||||
|
||||
async function doCmd(cmd, data) {
|
||||
const epdCmd = {
|
||||
EPD_CMD_CLR: 1,
|
||||
EPD_CMD_MODE: 2,
|
||||
EPD_CMD_BUF: 3,
|
||||
EPD_CMD_BUF_CONT: 4,
|
||||
EPD_CMD_LUT: 5,
|
||||
EPD_CMD_RST: 6,
|
||||
EPD_CMD_BW: 7,
|
||||
EPD_CMD_RED: 8,
|
||||
EPD_CMD_DP: 9,
|
||||
EPD_CMD_FILL: 10,
|
||||
|
||||
EPD_CMD_BUF_PUT: 11,
|
||||
EPD_CMD_BUF_GET: 12,
|
||||
EPD_CMD_SNV_WRITE: 13,
|
||||
EPD_CMD_SNV_READ: 14,
|
||||
|
||||
EPD_CMD_SAVE_CFG: 15,
|
||||
};
|
||||
|
||||
var chr = await epdService.getCharacteristic(0xfffe);
|
||||
switch (cmd) {
|
||||
case 'clr':
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_CLR]));
|
||||
break;
|
||||
|
||||
case 'mode':
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_MODE, data == 'image'?0x01:0x00]));
|
||||
break;
|
||||
|
||||
case 'buf':
|
||||
for (var i = 0; i < data.length; i += 60) {
|
||||
let arr = [(i == 0 ? epdCmd.EPD_CMD_BUF : epdCmd.EPD_CMD_BUF_CONT)];
|
||||
arr.push(...data.slice(i, i + 60));
|
||||
//console.log(arr);
|
||||
await chr.writeValueWithResponse(Uint8Array.from(arr));
|
||||
//info(`> buf at ${i} size ${arr.length}`)
|
||||
}
|
||||
break;
|
||||
|
||||
case 'lut':
|
||||
let arr = [epdCmd.EPD_CMD_LUT];
|
||||
arr.push(...data);
|
||||
await chr.writeValueWithResponse(Uint8Array.from(arr));
|
||||
break;
|
||||
|
||||
case 'rst':
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_RST]));
|
||||
break;
|
||||
|
||||
case 'bw':
|
||||
// write to bw ram
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_BW]));
|
||||
break;
|
||||
|
||||
case 'red':
|
||||
// write to bw ram
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_RED]));
|
||||
break;
|
||||
|
||||
case 'fill':
|
||||
// fill ram with black(0) or red(1)
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_FILL, data == 'red' ? 0x01:0x00]));
|
||||
break;
|
||||
|
||||
case 'dp':
|
||||
// show
|
||||
let lut = 0;
|
||||
switch (data) {
|
||||
case 'gray8':
|
||||
lut = 1;
|
||||
break;
|
||||
case 'user':
|
||||
lut = 0xff;
|
||||
break;
|
||||
case 'full':
|
||||
default:
|
||||
lut = 0;
|
||||
}
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_DP, lut]));
|
||||
break;
|
||||
|
||||
case 'snv_read':
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_SNV_READ, 0x80, 16]));
|
||||
break;
|
||||
|
||||
case 'snv_write':
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_SNV_WRITE, 0x80]));
|
||||
break;
|
||||
|
||||
case 'buf_get':
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_BUF_GET, 0x00]));
|
||||
break;
|
||||
|
||||
case 'buf_put':
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_BUF_PUT, 0x00, 0x01, 0x02, 0x03, 0x04]));
|
||||
break;
|
||||
|
||||
case 'save_cfg':
|
||||
await chr.writeValueWithResponse(Uint8Array.from([epdCmd.EPD_CMD_SAVE_CFG]));
|
||||
break;
|
||||
}
|
||||
info (`> epdCmd.${cmd}`)
|
||||
}
|
||||
|
||||
let ram_bw = [];
|
||||
let ram_red = [];
|
||||
function doImageGrey(type) {
|
||||
const canvas = document.getElementById('canvas');
|
||||
ram_bw = canvas2grey8(canvas, 'bw');
|
||||
ram_red = canvas2grey8(canvas, 'red');
|
||||
}
|
||||
|
||||
var step = 0;
|
||||
async function doUploadImageGray8() {
|
||||
await doCmd('clr');
|
||||
await sleep(15*1000);
|
||||
|
||||
doImageGrey();
|
||||
for (var i = 0; i < 8; i++) {
|
||||
await doCmd('rst');
|
||||
await sleep(2000);
|
||||
await doUploadImageRam8('bw');
|
||||
await doUploadImageRam8('red');
|
||||
await doCmd('dp', 'gray8');
|
||||
await sleep(8000);
|
||||
step++;
|
||||
}
|
||||
step = 0;
|
||||
info('> Upload done.')
|
||||
}
|
||||
async function doUploadImage(type) {
|
||||
if (type == 'gray8') {
|
||||
await doUploadImageGray8();
|
||||
} else {
|
||||
await doCmd('rst');
|
||||
await sleep(2000);
|
||||
await doUploadImageRam2('bw');
|
||||
await doUploadImageRam2('red');
|
||||
await doCmd('dp', 'full');
|
||||
await sleep(15*1000);
|
||||
}
|
||||
}
|
||||
|
||||
async function doUploadImageRam2(type = 'bw') {
|
||||
const canvas = document.getElementById('canvas');
|
||||
var arr = canvas2bytes(canvas, type = type);
|
||||
await doCmd('buf', arr);
|
||||
await doCmd(type)
|
||||
}
|
||||
|
||||
async function doUploadImageRam8(type = 'bw') {
|
||||
var ram = type == 'bw' ? ram_bw : ram_red;
|
||||
|
||||
// grey byte to bit
|
||||
var arr = [];
|
||||
var buffer = [];
|
||||
for (var x = 0; x < ram.length; x++) {
|
||||
const n = ram[x] > step ? 1 : 0;
|
||||
if (type == 'bw') {
|
||||
buffer.push(n ? 0 : 1);
|
||||
} else {
|
||||
buffer.push(n ? 1 : 0);
|
||||
}
|
||||
|
||||
if (buffer.length == 8) {
|
||||
arr.push(parseInt(buffer.join(''), 2));
|
||||
buffer = [];
|
||||
}
|
||||
}
|
||||
|
||||
info(`> write ram ${type} size ${arr.length}, step ${step}`);
|
||||
console.log(arr);
|
||||
await doCmd('buf', arr);
|
||||
await doCmd(type)
|
||||
}
|
||||
|
||||
function info(logTXT) {
|
||||
@@ -106,20 +311,173 @@
|
||||
document.getElementById("log").innerHTML = document.getElementById("log").innerHTML.substring(logs_br_position + 4);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>
|
||||
<label> Choose </label>
|
||||
<button id="btnConnect" type="button" onclick="doConnect()">Connect</button>
|
||||
</p>
|
||||
<p id="etagFn" style="visibility:hidden;">
|
||||
<button id="btnReadEtag" type="button" onclick="doReadEtag()">ReadEtag</button>
|
||||
<button id="btnSetTime" type="button" onclick="doSetTime()">SetTime</button>
|
||||
<button id="btnRtcCollab" type="button" onclick="doRtcCollab()">RtcCollab</button>
|
||||
</p>
|
||||
<p>
|
||||
<div id="log">
|
||||
CC2640R2-ETAG Webtool. <br/>
|
||||
</div>
|
||||
</p>
|
||||
async function load_image() {
|
||||
const image_file = document.getElementById('image_file');
|
||||
if (image_file.files.length > 0) {
|
||||
const file = image_file.files[0];
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const image = new Image();
|
||||
image.src = URL.createObjectURL(file);
|
||||
image.onload = function (event) {
|
||||
URL.revokeObjectURL(this.src);
|
||||
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
|
||||
//convert_dithering()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clear_canvas() {
|
||||
if (confirm('确认清除屏幕?')) {
|
||||
const canvas = document.getElementById('canvas');
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
}
|
||||
|
||||
function canvas2bytes(canvas, type = 'bw') {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const arr = [];
|
||||
let buffer = [];
|
||||
|
||||
for (let y = 0; y < canvas.height; y += 8) {
|
||||
for (let x = 0; x < canvas.width; x++) {
|
||||
for (let a = 0; a < 8; a++) {
|
||||
const i = (canvas.width * (y + a) + x) * 4;
|
||||
if (type !== 'red') {
|
||||
// 1 for white, 0 for black
|
||||
// black : 0, 0, 0
|
||||
buffer.push(imageData.data[i] === 0 && imageData.data[i + 1] === 0 && imageData.data[i + 2] === 0 ? 0 : 1);
|
||||
} else {
|
||||
// 1 for red, 0 for white
|
||||
buffer.push(imageData.data[i] > 0 && imageData.data[i + 1] === 0 && imageData.data[i + 2] === 0 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
arr.push(parseInt(buffer.join(''), 2));
|
||||
buffer = [];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function canvas2grey8(canvas, type = 'bw') {
|
||||
// each px = 4bit black + 4bit red.
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const arr = [];
|
||||
let buffer = [];
|
||||
|
||||
for (let y = 0; y < canvas.height; y += 8) {
|
||||
for (let x = 0; x < canvas.width; x++) {
|
||||
for (let a = 0; a < 8; a++) {
|
||||
const i = (canvas.width * (y + a) + x) * 4;
|
||||
const R = imageData.data[i];
|
||||
const G = imageData.data[i + 1];
|
||||
const B = imageData.data[i + 2];
|
||||
|
||||
let grey = 0; // white
|
||||
if (R == 255 && G != 255 && B != 255) { // red
|
||||
grey = (type == 'bw') ? 0 : (510 + 62 - G - B) >> 6;
|
||||
} else { // gray
|
||||
grey = (type == 'bw') ? (255 + 31 - R) >> 5 : 0;
|
||||
}
|
||||
arr.push(grey);
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
async function doMyLut() {
|
||||
const O = 0b00000000; // VSS
|
||||
const B = 0b01000000; // VSH1 for Black
|
||||
const W = 0b10000000; // VSL for white
|
||||
const R = 0b11000000; // VSH2 for Red
|
||||
const lut_gray8 = [
|
||||
// RP A B C D SRAB SRCD
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUTC
|
||||
0x01, R | 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, // LUTR
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUTW
|
||||
0x01, B | 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, // LUTB
|
||||
0x01, B | 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, // LUTB
|
||||
];
|
||||
await doCmd('lut', lut_gray8);
|
||||
}
|
||||
|
||||
async function doFill(color) {
|
||||
await doCmd('rst');
|
||||
await sleep(2000);
|
||||
await doCmd('fill', color);
|
||||
await doCmd('dp', 'full');
|
||||
await sleep(15*1000);
|
||||
}
|
||||
|
||||
async function doSnvRead(type) {
|
||||
var chr = await epdService.getCharacteristic(0xfffe);
|
||||
if (type == 'cfg') {
|
||||
await doCmd('snv_read', 0x01);
|
||||
await doCmd('buf_get', 0x00);
|
||||
var a = await chr.readValue();
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
|
||||
async function doSnvWrite() {
|
||||
//var chr = await epdService.getCharacteristic(0xfffe);
|
||||
await doCmd('buf_put', 0x00);
|
||||
await doCmd('snv_write', 0x80);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<label> Choose </label>
|
||||
<button id="btnConnect" type="button" onclick="doConnect()">Connect</button>
|
||||
</div>
|
||||
<div id="etagFn" style="visibility:hidden;">
|
||||
<button type="button" onclick="doCmd('mode', 'clock')">时钟模式</button>
|
||||
<button id="btnReadEtag" type="button" onclick="doReadEtag()">ReadEtag</button>
|
||||
<button id="btnSetTime" type="button" onclick="doSetTime()">SetTime</button>
|
||||
<button id="btnRtcCollab" type="button" onclick="doRtcCollab()">RtcCollab</button>
|
||||
<button id="btnTest" type="button" onclick="doTest()">Test</button>
|
||||
<button type="button" onclick="doCmd('save_cfg')">保存设置</button>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" onclick="doCmd('mode', 'image')">图片模式</button>
|
||||
<button type="button" onclick="doCmd('rst')">rst</button>
|
||||
<button type="button" onclick="doCmd('bw')">bw</button>
|
||||
<button type="button" onclick="doCmd('red')">red</button>
|
||||
<button type="button" onclick="doCmd('dp', 'full')">dp</button>
|
||||
<button type="button" onclick="doMyLut()">LUT</button>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" onclick="doCmd('clr')">清屏</button>
|
||||
<button type="button" onclick="doFill('black')">全黑</button>
|
||||
<button type="button" onclick="doFill('red')">全红</button>
|
||||
</div>
|
||||
<div id="canvas-box">
|
||||
<input type="file" id="image_file" onchange="load_image()" accept=".png,.jpg,.bmp,.webp,.gif">
|
||||
<br>
|
||||
<canvas id="canvas" width="296" height="128" style="border: black solid 1px;"></canvas>
|
||||
<br>
|
||||
<button onclick="doUploadImage('gray8')">8级灰度</button>
|
||||
<button onclick="doUploadImage('full')">默认刷新</button>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="button" onclick="doSnvWrite()">写入</button>
|
||||
<button type="button" onclick="doSnvRead('cfg')">读出</button>
|
||||
</div>
|
||||
<div id="log">
|
||||
CC2640R2-ETAG Webtool. <br>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
BIN
tools/pic/black.bmp
Normal file
|
After Width: | Height: | Size: 111 KiB |
74
tools/pic/gen_bmp.py
Normal file
@@ -0,0 +1,74 @@
|
||||
import os
|
||||
import struct
|
||||
|
||||
def flatten_list(nested_list):
|
||||
flat_list = []
|
||||
for item in nested_list:
|
||||
if isinstance(item, list):
|
||||
flat_list.extend(flatten_list(item))
|
||||
else:
|
||||
flat_list.append(item)
|
||||
return flat_list
|
||||
|
||||
def pkg(fmt, num):
|
||||
a = struct.pack(fmt, num)
|
||||
return [ x for x in a ]
|
||||
|
||||
def rgb(r, g, b):
|
||||
return [b, g, r]
|
||||
|
||||
def genTestBmp(fpath, type = 0):
|
||||
width = 296
|
||||
height = 128
|
||||
|
||||
# header
|
||||
out = [
|
||||
0x42, 0x4D,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x36, 0x00, 0x00, 0x00, # pixel data offset at 0x36
|
||||
]
|
||||
|
||||
out += [
|
||||
0x28, 0x00, 0x00, 0x00, # header size 40 bytes
|
||||
pkg('<I', width),
|
||||
pkg('<I', height),
|
||||
0x01, 0x00, # plane 1
|
||||
0x18, 0x00, # bpp 24 bits
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
]
|
||||
|
||||
if type == 0:
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
bg = ((x+1)//33)*32
|
||||
bg = bg - 1 if bg else 0
|
||||
if y > height/2:
|
||||
out += rgb(255, bg, bg)
|
||||
else:
|
||||
out += rgb(bg, bg, bg)
|
||||
# TBD: pad for 4 bytes alignment.
|
||||
|
||||
elif type == 1: # all black
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
out += rgb(0, 0, 0)
|
||||
|
||||
elif type == 2: # all red
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
out += rgb(255, 0, 0)
|
||||
|
||||
with open(fpath, 'wb') as f:
|
||||
f.write(bytes(flatten_list(out)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
genTestBmp('grey.bmp', type = 0)
|
||||
genTestBmp('black.bmp', type = 1)
|
||||
genTestBmp('red.bmp', type = 2)
|
||||
BIN
tools/pic/grey.bmp
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
tools/pic/red.bmp
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
tools/pic/可莉.gif
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
tools/pic/可莉2.gif
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
tools/pic/可莉3.gif
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
tools/pic/水神1.gif
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
tools/pic/灰度测试.gif
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
tools/pic/灰度测试2.gif
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
tools/pic/绫华1.gif
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
tools/pic/胡桃1.gif
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
tools/pic/胡桃2.gif
Normal file
|
After Width: | Height: | Size: 8.6 KiB |