DALI send/recv library.

Dependents:   dali_sample

DALI send/recv library

lighting control protocol.

設備照明の調光プロトコル DALI を送受信するライブラリです。

DALI インターフェースの回路図などは次を参照。

DALI.cpp

Committer:
okini3939
Date:
2020-07-22
Revision:
0:6cb7026982fc
Child:
1:319d52b5116b

File content as of revision 0:6cb7026982fc:

/*
 * DALI send/recv library
 * Copyright (c) 2020 Hiroshi Suga
 * Released under the MIT License: http://mbed.org/license/mit
 */

/** @file
 * @brief DALI send/recv
 */
 
#include "mbed.h"
#include "DALI.h"

#define BUFFER_SIZE 20

#define TIME_BITTIME 833 // us, 1200bps
#define TIME_BITTIME1p2 417 // us, 1200bps 3/4
#define TIME_BITTIME3p4 625 // us, 1200bps 3/4

extern Serial pc;

DALI::DALI (PinName tx, PinName rx) : _tx(tx), _rx(rx) {
    _rx.mode(PullUp);
    _rx.fall(this, &DALI::isr_rx);
    _rx.rise(this, &DALI::isr_rx);
    _tx = 0;

    recv_buf = new CircBuffer<int>(BUFFER_SIZE);
    send_buf = new CircBuffer<int>(BUFFER_SIZE);

    mode = 0;
    count = 0;
    timeflg = 0;
    busy = 0;
}

int DALI::read (enum DALI_FRAME *frame, int *addr, int *value) {
    int dat;

    if (recv_buf->dequeue(&dat)) {
        if (dat & (1<<16)) {
            *frame = DALI_BACKWARD;
            *value = dat & 0xff;
        } else {
            if (dat & (1<<15)) {
                // group
                if (dat & (1<<8)) {
                    *frame = DALI_FORWARD_GROUP_IAP;
                } else {
                    *frame = DALI_FORWARD_GROUP_DAP;
                }
            } else {
                // short
                if (dat & (1<<8)) {
                    *frame = DALI_FORWARD_SHORT_IAP;
                } else {
                    *frame = DALI_FORWARD_SHORT_DAP;
                }
            }
            *addr = (dat >> 9) & 0x3f;
            *value = dat & 0xff;
        }
        return 0;
    }
    return -1;
}

int DALI::readable () {
    return ! recv_buf->isEmpty();
}

void DALI::isr_send () {

    if (send_bit & 3) {
        // stop bit
        _tx = 0;
    } else
    if ((halfbit == 0 && (send_data & send_bit)) || (halfbit == 1 && !(send_data & send_bit))) {
        // low
        _tx = 1;
    } else {
        // high
        _tx = 0;
    }

    if (halfbit) {
        halfbit = 0;
        send_bit >>= 1;

        if (send_bit == 0) {
            if (send_buf->isEmpty()) {
                // end
                _ticker.detach();
                busy = 0;
            } else {
                // next
                int dat, bit;
                send_buf->dequeue(&dat);
                bit = (dat >> 20) & 0x0fff;
                send_bit = (1 << bit) << 2;
                send_data = dat & 0x0fffff;
                halfbit = 0;
            }
        }
    } else {
        halfbit = 1;
    }
}

int DALI::write (enum DALI_FRAME frame, int addr, int value) {
    int dat, bit;

    while ((volatile int)send_bit);

    switch (frame) {
    case DALI_FORWARD_SHORT_DAP:
        dat = (0<<15) | ((addr & 0x3f) << 9) | (0<<8) | (value & 0xff);
        bit = 16;
        break;

    case DALI_FORWARD_SHORT_IAP:
        dat = (0<<15) | ((addr & 0x3f) << 9) | (1<<8) | (value & 0xff);
        bit = 16;
        break;

    case DALI_FORWARD_GROUP_DAP:
        dat = (1<<15) | ((addr & 0x3f) << 9) | (0<<8) | (value & 0xff);
        bit = 16;
        break;

    case DALI_FORWARD_GROUP_IAP:
        dat = (1<<15) | ((addr & 0x3f) << 9) | (1<<8) | (value & 0xff);
        bit = 16;
        break;

    case DALI_BACKWARD:
        dat = (value & 0xff);
        bit = 8;
        break;

    default:
        return -1;
    }

    dat = ((1<<bit) | dat) << 2; // start bit
    dat |= bit << 20;
    send_buf->queue(dat);

    if (! busy) {
        busy = 1;
        send_buf->dequeue(&dat);
        bit = (dat >> 20) & 0x0fff;
        send_bit = (1 << bit) << 2;
        send_data = dat & 0x0fffff;
        halfbit = 0;
        _ticker.attach_us(this, &DALI::isr_send, TIME_BITTIME1p2);
    }
    return 0;
}

void DALI::isr_rx () {

    if (timeflg || busy) return;

    timeflg = 1;
    _timer.detach();
    _timer.attach_us(this, &DALI::isr_timer, TIME_BITTIME3p4);

    if (_rx.read() && mode == 0) {
        // start bit
        mode ++;
        recv_data = 0;
        recv_bit = 0x8000;
    }
}

void DALI::isr_timer () {
    timeflg = 0;
    _timer.detach();
    _timer.attach_us(this, &DALI::isr_timeout, TIME_BITTIME);

    if (recv_bit) {
        // data bit
        if (_rx.read()) {
            // high
            recv_data |= recv_bit;
        } else {
            // low
            recv_data &= ~recv_bit;
        }
        recv_bit >>= 1;
    } else
    if (mode == 1) {
        if (_rx.read()) {
            // error
            mode = 0;
        } else {
            // stop bit 1
            mode ++;
        }
    }
}

void DALI::isr_timeout () {
    timeflg = 0;
    _timer.detach();

    if (recv_bit) {
        // Backward frame (8bit)
        if (mode == 1 && _rx.read() == 0 && !(recv_data & (1<<7))) {
            // stop bit 2
            recv_data = (1<<16) | (recv_data >> 8);
            recv_buf->queue(recv_data);
            mode = 0;
        } else {
            mode = 0;
        }
    } else {
        // Forward frame (16bit)
        if (mode == 2 && _rx.read() == 0) {
            // stop bit 2
            recv_buf->queue(recv_data);
            mode = 0;
        } else {
            mode = 0;
        }
    }
}