2011年6月アーカイブ

少し汚れている気がしたのでメンテナンスしました。

手順(メーカー推奨というわけではなく、勝手メンテです。同じ手順で壊しても知りません。)
・ネジ8本外す
・ガラスを外す
 向かって右側にツメがあるので、右側に寄せて左側を持ち上げます
・ブロアで内部のホコリを吹き飛ばす
・受光部と反射用の白い部分をアルコールで拭きとる
・ブロアで受光部まわりのホコリを吹き飛ばす
・目視でゴミが無いか確認
・ガラスの裏面をアルコールで拭きとり
・ブロアでゴミを飛ばす
・目視でゴミが無いか確認
・取り付け
 手前2つのCISユニットは少し奥に押して、ガラスの金具を割り込ませる
・目視でゴミが無いか確認
・ネジ止め
・ガラス表面をアルコールで拭きとる
・キャリブレーションをやり直す

20110701-P1020833.jpg

タイマー割り込みで制御するようにしました。
TIMER7によって、0.5ms毎に割り込みがかかります。

排他制御のために、FreeRTOSのセマフォを使っています。
でも、boolの書き込みはきっと大丈夫だろうと、ちょっと適当です。

ヘッダ

typedef enum SRK229Mode
{
    SRK229MODE_AUTO,
    SRK229MODE_COLD,
    SRK229MODE_DRY,
    SRK229MODE_HOT
} SRK229Mode;

typedef enum SRK229Power
{
    SRK229POWER_AUTO,
    SRK229POWER_HIGH,
    SRK229POWER_MIDDLE,
    SRK229POWER_LOW
} SRK229Power;

typedef enum SRK229Flow
{
    SRK229FLOW_DEFAULT,
    SRK229FLOW_AUTO,
    SRK229FLOW_NONE
} SRK229Flow;

typedef enum SRK229Timer
{
    SRK229TIMER_DISABLE,
    SRK229TIMER_STOP,
    SRK229TIMER_START
} SRK229Timer;

// 送信を指定するデータです
typedef struct SRK229Data
{
    int temperature;
    SRK229Mode Mode;
    SRK229Power Power;
    SRK229Flow Flow;
    SRK229Timer Timer;
    int TimerHour;
} SRK229Data;

// 初期化します
// SendDataの前に必ず1度だけ実行してください
void SRK229Init(int port, bool isInverted);

// データを送信します
bool SRK229SendData(const SRK229Data *data);

// テストです
void SRK229Test();

ソース



#include "stm32f10x_conf.h"
#include "I2CRoutines.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

#include "srk229.h"

#define true (1)
#define false (0)

typedef struct SRK229Param
{
    int IrPort;
    bool IsInverted;
} SRK229Param;

SRK229Param param;


xSemaphoreHandle xSemaphore; // 以下のデータの排他制御用
bool IsRunning = 0; // 送信中
unsigned char RawData[64]; // 送信用の低レベルデータ
int TotalBit = 0; // RawDataのビット数
int RunBit = 0; // 送信済みビット数

// 2kHzの割り込み関数
void TIM7_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM7, TIM_IT_Update);

        if (!IsRunning)
        {
            return;
        }

        if (RunBit == TotalBit)
        {
            if (param.IsInverted)
            {
                GPIOA->BSRR =_BV(param.IrPort);
            }
            else
            {
                GPIOA->BRR =_BV(param.IrPort);
            }
            IsRunning = 0;
        }
        else
        {
            int offsetByte = RunBit / 8;
            int offsetBit = RunBit % 8;

            if (RawData[offsetByte] & (1 << (7 - offsetBit)))
            {
                if (param.IsInverted)
                {
                    GPIOA->BRR = _BV(param.IrPort);
                }
                else
                {
                    GPIOA->BSRR =_BV(param.IrPort);
                }
            }
            else
            {
                if (param.IsInverted)
                {
                    GPIOA->BSRR =_BV(param.IrPort);
                }
                else
                {
                    GPIOA->BRR = _BV(param.IrPort);
                }
            }

            ++RunBit;
        }
    }
}

// RawDataを初期化する
void ResetRawData()
{
    TotalBit = 0;
}

// RawDataに追加する
void CreateRawData(int ms, int active)
{
    for (; ms > 0; ms -= 5)
    {
        int offsetByte = TotalBit / 8;
        int offsetBit = TotalBit % 8;

        if (offsetBit == 0)
        {
            RawData[offsetByte] = 0;
        }

        if (active)
        {
            RawData[offsetByte] |= 1 << (7 - offsetBit);
        }

        ++TotalBit;
    }
}

// RawDataを実行に移す
void EnqueueRawData()
{
    RunBit = 0;
    IsRunning = 1;
}

// 送信準備を行う
void SRK229SendIr(unsigned char *buffer, size_t size)
{
    if (size <= 0)
    {
        return;
    }

    ResetRawData();

    // 全長は、60 + 75 + 8 * (0.5 + 3.5 + 0.5 + 1.5) * size + 5 + 75 + 5
    // = 220 + 48 * size
    // sizeは6が最大なので、508 bit, 64 byteのデータになります

    // START
    CreateRawData(60, 1); // 6ms ON
    CreateRawData(75, 0); // 7.5ms OFF

    int i;
    for (i = size - 1; i >= 0; --i)
    {
        int j;
        for (j = 1; j < 0x100; j <<= 1)
        {
            CreateRawData(5, 1); // 0.5mfs

            if ((buffer[i] & j) == 0) // 反転
            {
                CreateRawData(35, 0); // 3.5ms
            }
            else
            {
                CreateRawData(15, 0); // 1.5ms
            }
        }

        for (j = 1; j < 0x100; j <<= 1)
        {
            CreateRawData(5, 1); // 0.5ms

            if ((buffer[i] & j) != 0)
            {
                CreateRawData(35, 0); // 3.5ms
            }
            else
            {
                CreateRawData(15, 0); // 1.5ms
            }
        }
    }

    // STOP
    CreateRawData(5, 1); // 0.5ms
    CreateRawData(75, 0); // 7.5ms
    CreateRawData(5, 1); // 0.5m

    EnqueueRawData();
}

// リモコンデータを生成して、送信準備を行う
bool SRK229SendData(const SRK229Data *data)
{
    if (xSemaphore == NULL)
    {
        return false;
    }
    if (xSemaphoreTake(xSemaphore, 10) != pdTRUE)
    {
        return false;
    }

    if (IsRunning)
    {
        return false;
    }

    if (data->temperature < 18 || data->temperature > 30)
    {
        return false;
    }
    if (data->Timer != SRK229TIMER_DISABLE &&
        (data->TimerHour < 1 || data->TimerHour > 12))
    {
        return false;
    }

    unsigned char irData[5];
    int size = 0;

    irData[size++] = 0xd5;

    irData[size] = 0;
    irData[size] |= (data->temperature - 17) << 4;
    irData[size] |= 1 << 3;
    irData[size] |= data->Mode;
    ++size;

    irData[size] = 0;
    irData[size] |= 0 << 7;
    irData[size] |= data->Power << 5;
    irData[size] |= data->Flow << 3;
    irData[size] |= 0;
    ++size;

    SRK229SendIr(irData, size);

    xSemaphoreGive(xSemaphore);

    return true;
}

GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

// 初期化
// 以下の設定が別途必要です。
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// NVIC_SetPriority(TIM7_IRQn, 0x01);
// NVIC_EnableIRQ(TIM7_IRQn);
void SRK229Init(int port, bool isInverted)
{
    param.IrPort = port;
    param.IsInverted = isInverted;

    xSemaphore = xSemaphoreCreateMutex();

    GPIO_InitStructure.GPIO_Pin = _BV(2);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    if (param.IsInverted)
    {
        GPIOA->BSRR = _BV(param.IrPort);
    }
    else
    {
        GPIOA->BRR = _BV(param.IrPort);
    }

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);

    // 2000Hz = 72M / 36k
    TIM_DeInit(TIM7);
    TIM_TimeBaseStructure.TIM_Period = 50 - 1;
    TIM_TimeBaseStructure.TIM_Prescaler = 720 - 1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIM7, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM7, TIM_OCPreload_Enable);

    TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);
  
    TIM_Cmd(TIM7, ENABLE);

}

// テスト
void SRK229Test()
{
    SRK229Data data;
    data.Flow = SRK229FLOW_AUTO;
    data.Mode = SRK229MODE_COLD;
    data.Power = SRK229POWER_AUTO;
    data.temperature = 28;
    data.Timer = SRK229TIMER_DISABLE;
    data.TimerHour = 0;

    SRK229SendData(&data);
  
    while (1);
}



とりあえず動きます。タイマーには対応していません。
configTICK_RATE_HZを2000にする必要があります。
FreeRTOSを使っています。

割り込みで自前で処理しないとちょっとタイミングが怪しそう。
自分でクロックを作る場合は遅延もあまり問題にならないけど、
赤外線の場合、あまり遅れると相手側で認識されない。

メイン側

    SRK229ParamSetup(&srk229param, 2, true);
    SRK229TaskInit(&srk229param);
    SRK229Test(&srk229param);

ヘッダ

typedef struct SRK229Param
{
    int IrPort;
    bool IsInverted;
} SRK229Param;

void SRK229ParamSetup(SRK229Param *param, int port, bool isInverted);

void SRK229TaskInit(SRK229Param *param);
void SRK229Test(void *param);


ライブラリ側ソース

#include "stm32f10x_conf.h"
#include "I2CRoutines.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#include "srk229.h"

#define true (1)
#define false (0)

void SRK229ParamSetup(SRK229Param *param, int port, bool isInverted)
{
    param->IrPort = port;
    param->IsInverted = isInverted;
}

typedef enum SRK229Mode
{
    SRK229MODE_AUTO,
    SRK229MODE_COLD,
    SRK229MODE_DRY,
    SRK229MODE_HOT
} SRK229Mode;

typedef enum SRK229Power
{
    SRK229POWER_AUTO,
    SRK229POWER_HIGH,
    SRK229POWER_MIDDLE,
    SRK229POWER_LOW
} SRK229Power;

typedef enum SRK229Flow
{
    SRK229FLOW_DEFAULT,
    SRK229FLOW_AUTO,
    SRK229FLOW_NONE
} SRK229Flow;

typedef enum SRK229Timer
{
    SRK229TIMER_DISABLE,
    SRK229TIMER_STOP,
    SRK229TIMER_START
} SRK229Timer;

typedef struct SRK229Data
{
    int temperature;
    SRK229Mode Mode;
    SRK229Power Power;
    SRK229Flow Flow;
    SRK229Timer Timer;
    int TimerHour;
} SRK229Data;

void WaitMicroSeconds(unsigned int ns)
{
    unsigned int tick = configTICK_RATE_HZ / 1000 * ns / 1000;
    vTaskDelay(tick);
}

void SRK229ActiveIrSignal(const SRK229Param *param)
{
    if (param->IsInverted)
    {
        GPIOA->BRR = _BV(param->IrPort);
    }
    else
    {
        GPIOA->BSRR = _BV(param->IrPort);
    }
}

void SRK229DeactiveIrSignal(const SRK229Param *param)
{
    if (param->IsInverted)
    {
        GPIOA->BSRR = _BV(param->IrPort);
    }
    else
    {
        GPIOA->BRR = _BV(param->IrPort);
    }
}

void SRK229SendIr(const SRK229Param *param, unsigned char *buffer, size_t size)
{
    if (size <= 0)
    {
        return;
    }

    // START
    SRK229ActiveIrSignal(param);
    WaitMicroSeconds(6000); // 6msec

    SRK229DeactiveIrSignal(param);
    WaitMicroSeconds(7500); // 約7.4msec

    int i;
    for (i = size - 1; i >= 0; --i)
    {
        int j;
        for (j = 1; j < 0x100; j <<= 1)
        {
            SRK229ActiveIrSignal(param);
            WaitMicroSeconds(500); // 0.5msec

            SRK229DeactiveIrSignal(param);
            if ((buffer[i] & j) == 0) // 反転
            {
                WaitMicroSeconds(3500); // 3.5msec
            }
            else
            {
                WaitMicroSeconds(1500); // 1.5msec
            }
        }

        for (j = 1; j < 0x100; j <<= 1)
        {
            SRK229ActiveIrSignal(param);
            WaitMicroSeconds(500); // 0.5msec

            SRK229DeactiveIrSignal(param);
            if ((buffer[i] & j) != 0)
            {
                WaitMicroSeconds(3500); // 3.5msec
            }
            else
            {
                WaitMicroSeconds(1500); // 1.5msec
            }
        }
    }

    // STOP
    SRK229ActiveIrSignal(param);
    WaitMicroSeconds(500); // 0.5msec

    SRK229DeactiveIrSignal(param);
    WaitMicroSeconds(7500); // 約7.4msec

    SRK229ActiveIrSignal(param);
    WaitMicroSeconds(500); // 0.5msec

    SRK229DeactiveIrSignal(param);
}


bool SRK229SendData(const SRK229Param *param, const SRK229Data *data)
{
    if (data->temperature < 18 || data->temperature > 30)
    {
        return false;
    }
    if (data->Timer != SRK229TIMER_DISABLE &&
        (data->TimerHour < 1 || data->TimerHour > 12))
    {
        return false;
    }

    unsigned char irData[5];
    int size = 0;

    irData[size++] = 0xd5;

    irData[size] = 0;
    irData[size] |= (data->temperature - 17) << 4;
    irData[size] |= 1 << 3;
    irData[size] |= data->Mode;
    ++size;

    irData[size] = 0;
    irData[size] |= 0 << 7;
    irData[size] |= data->Power << 5;
    irData[size] |= data->Flow << 3;
    irData[size] |= 0;
    ++size;

    SRK229SendIr(param, irData, size);


    return true;
}

// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);は実行しておいてください
void SRK229TaskInit(SRK229Param *param)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = _BV(param->IrPort);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    SRK229DeactiveIrSignal(param);
}

void SRK229Test(void *param)
{
    SRK229Data data;
    data.Flow = SRK229FLOW_AUTO;
    data.Mode = SRK229MODE_COLD;
    data.Power = SRK229POWER_AUTO;
    data.temperature = 28;
    data.Timer = SRK229TIMER_DISABLE;
    data.TimerHour = 0;
    SRK229SendData((SRK229Param*)param, &data);
}

とりあえず送信部分の設計。

かなり適当な搬送波でも良いので、555で搬送波を作って、
面倒なのでRESET入力で信号のON/OFFを制御。
555が5V動作なので、この部分は5V動作です。

LEDに流すのは100mAくらいなので
555のOUTに直接繋いでも定格内ですが、
ちょっと気になるのでC1815でドライブしています。

バッテリ駆動する気がなかったので、あまり省電力ではありません。

STM32Fの出力はOD設定を想定していますが、PPでも大丈夫です。

マイコン出力の論理とIR出力を一致させようとしたのだけど、
OD前提だと、HiZ = H で、信号出っぱなし。
ちょっと誤ったかも。

というわけで、Lで出力するように変更。
ir2.PNG

SHT-1x/7x で温度や湿度を取得値から変換する式が

仕様書のバージョンによって異なります。

どのような違いがあるか調べてみます。

 

2010 MayVersion4.3のマニュアルでは下式です。

RH_linear = c1 + c2 SO_RH + c3 SO_RH^2

 

c1 = -2.0468

c2 = 0.0367

c3 = -1.5955E-6

 

RH_true = (T - 25) (t1 + t2 SO_RH) + RH_linear

 

t1 = 0.01

t2 = 0.00008

 

T = d1 + d2 SO_T

 

3.5V

d1 = -39.7

d2 = 0.01

 

2007 MarVersion3.0でのパラメータは少し異なって、

 

c1 = -4

c2 = 0.0405

c3 = -2.8E-6

 

d1 = -39.66

d2 = 0.01

 

です。

 

温度が 0.04度違うと、

0.0004 + 3.2 *10^-6 SO_RH

だけ補償の値が変わります。

最大値でも、0.01%くらいしか変わりませんので

無視できます。

 

(v3.0RH_linearの式) - (v4.3RH_linearの式)

= -1.9532 + 0.0038 SO_RH - 1.2045E-6 SO_RH^2

です。


SHT71_v3_v4.gif


+1% から -2% くらい両式で異なります。

誤差を考えると、v3v4.3のどちらでも良さそうです。

昨日はちまちまと解析したけど、
ZEROPLUSのLogicCubeなら自前のプロトコル解析もSDKで作れるはずなので、
ちょっと調べてみました。

試しに作ったプロトコルは、到底公開できるレベルにはなっていませんが、
ある程度決め打ちで解析するなら数時間あれば対応できました。
解析部分は、各入力のエッジが順番に取れるので、
かなり簡単に書けます。

リモコンデータ(PPM)に対応した例。


remcon_lapc.gif


動作実験レベルですが、今回書いたコードは以下のような感じです。
sda_index = 0;
unsigned int byteStartPos = 0;
int byteBitCount = 0;
int byteData = 0;
unsigned int start_pos = 0;
for(;sda_index < sda_array_size;)
{
start_pos = pSubItemA->GetPosByAryIndex(sda_index++);

sda_edge_type = pSubItemA->GetEdgeType(start_pos);
if (sda_edge_type == RAISING_EDGE)
{
unsigned int prev_pos = start_pos;
unsigned next_pos;
while (1)
{
do
{
next_pos = pSubItemA->GetPosByAryIndex(sda_index++);
if (next_pos >= nSize) return true;
} while (pSubItemA->GetEdgeType(next_pos) != RAISING_EDGE);

if (next_pos - prev_pos < 10)
{
prev_pos = next_pos;
continue;
}
break;
}

if (!findStart && next_pos - start_pos > 1000)
{
nFlag=LABEL_START;
nIndex=Add(start_pos,0,nFlag,Getclr(nFlag));
findStart = true;
}
else
{
if (byteBitCount == 0)
{
byteStartPos = start_pos;
}

int bit = 0;
if (next_pos - start_pos > 300)
bit = 1;
--sda_index;

byteData |= (bit << byteBitCount);
++byteBitCount;
if (byteBitCount == 8)
{
nFlag=LABEL_ADDRESS;
nIndex=Add(byteStartPos, byteData,nFlag,Getclr(nFlag));

nFlag=LABEL_UNKNOW;
nIndex=Add(next_pos - 1,0,nFlag,Getclr(nFlag));

byteBitCount = 0;
byteData = 0;
}
}
}
}

VisualC++ 6.0推奨みたいですが、VisualStudio2010でも問題なく動いてます。
いまさらVC6を入れるのは面倒だし、使いにくいので、助かりました。
ただ、ビルドして出来上がるDLLは何故か大きいです。

パルス幅とかハードコーディングです。
設定ダイアログも作れるのですが、MFCで書くので結構面倒です。


このリモコン用のプロトコルを真面目につくっても
あまり利用場面は無さそうなので、これ以上は作りこまないし公開もしません。
赤外線リモコンの一般的なプロトコルであるNECフォーマットについては、
公式のNEC PD6122プロトコルで多分大丈夫です。

空調の自動化のために、

ビーバーエアコンSRK229のデータを調べました。

 

ロジアナでちまちま調べたので間違ってるかもしれません。

 

 

搬送波 28kHz (デューティー比30%くらい)

 

構造

Leader

Data

Stop

 

Leaderは、

6ms ON

7.35ms OFF

 

Stopは、

0.50ms ON

7.45ms OFF

0.50ms ON

 

Dataは、

Bit 1: 0.50ms ON => 3.45ms OFF

Bit 0: 0.50ms ON => 1.45ms OFF

LSB -> MSB

さらにバイト順も下位バイトから送られます。

また、1バイト毎に反転データ8bit、データ8bit16bitで送られます。

3バイトのデータなら、

!b0 !b1 !b2 !b3 !b4 !b5 !b6 !b7

b0 b1 b2 b3 b4 b5 b6 b7

!b8 !b9 !b10 !b11 !b12 !b13 !b14 !b15

b8 b9 b10 b11 b12 b13 b14 b15

!b16 !b18 !b18 !b19 !b20 !b21 !b22 !b23

b16 b17 b18 b19 b20 b21 b22 b23

と送られます。

 

以下の説明では、すべてビット順を並び替えたものとして行います。

 

まず、8bit 0xD5から始まります。

 

次に4bitで温度設定です。

連続が0

18度が1  (18-17=1)

30度がD  (30-17=13)

自動の場合、

連続はなく、

-6 1

0 7

+6 13


1bit ON/OFF

0 OFF

1 ON

 

3bit 動作モード

自動 0    (0b000)

冷房 1    (0b001)

暖房 4    (0b100)

ドライ 2 (0b010)

 

1bit  0

 

2bit 風量

0 自動

1

2

3

 

2bit 風向

0 風向あり

1 全自動

2 風向なし

 

3bit

0 ならタイマー設定なしでデータ終了

2 ならタイマー設定がこの後に24bit続く

 

タイマーの場合

12bit 切タイマー時間

12bit 入タイマー時間

 

時間は、分単位

1h 3C

2h 78

3h B4

4h F0

5h 12C

6h 168

7h 1A4

8h 1E0

9h 21C

10h 258

11h 294

12h 2D0

 

たとえば、

冷房 28度 風向あり 風量自動 タイマーなしの場合、

D5 B9 00

なので、赤外線で送るデータは後ろから

STOP D5 2A B9 46 00 FF START (こちらが先頭)

です。


フォルダで使えない文字
*:\"<>?|/

アイテムで使えない文字は、表示可能な範囲ではありません。

表示可能でない文字(制御文字)は調べていません。

このアーカイブについて

このページには、2011年6月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2011年5月です。

次のアーカイブは2011年8月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。