STM32マイコンでAHT10温湿度センサーデータを読み取る

STM32がハードウェアI2CでAHT10温湿度センサからデータを読み取り、0.96インチOLEDディスプレイに表示する。

使用したマイコンはSTM32F103C8T6で、プログラムはST標準ライブラリで記述した。

STM32がハードウェアI2CでSHTC3温湿度センサを読む例:https://blog.zeruns.com/archives/692.html

電子・マイコン技術交流QQ群:2169025065

動作イメージ


I2Cプロトコル概要

I2C通信プロトコル(Inter-Integrated Circuit)はPhilips社が開発したもので、ピン数が少なく、ハードウェア実装が簡単で拡張性が高く、USARTやCANなどの外部トランシーバ(レベル変換IC)が不要なため、現在ではシステム内の複数のIC間通信に広く使われている。

I2Cはデータ用の単一バス線SDA(Serial Data Line)しかなく、1ビットずつデータを送信するシリアル通信で、半二重通信を採用している。

半二重通信:双方向通信が可能だが、同時に双方向にはできず、交互に切り替える必要がある。切り替え可能な単一方向通信と考えてもよく、同一時刻には一方向の転送しかできないため、データ線は1本で済む。

I2C通信プロトコルは物理層とプロトコル層に分けられる。物理層は通信システムの機械・電子的特性(ハードウェア部分)を規定し、物理メディアでの生データ転送を保証する。プロトコル層は通信ロジックを規定し、送受信側のデータパケット化・展開標準(ソフトウェアレベル)を統一する。

I2C物理層

I2C通信デバイスの一般的な接続方法

(1) 複数デバイスをサポートするバス。「バス」とは複数デバイスが共用する信号線のこと。1つのI2C通信バスには複数のI2C通信デバイスを接続でき、複数のマスターおよびスレーブをサポートする。

(2) I2Cバスは2本のバス線のみを使用:双方向シリアルデータ線SDA(Serial Data Line)とシリアルクロック線SCL(Serial Clock Line)。データ線はデータを表し、クロック線はデータ送受信の同期に使う。

(3) バスはプルアップ抵抗で電源に接続される。I2Cデバイスがアイドル時はハイインピーダンス出力となり、すべてのデバイスがアイドルでハイインピーダンス出力のとき、プルアップ抵抗によってバスはハイレベルに引き上げられる

I2C通信時、マイコンのGPIOピンは開漏れ出力に設定する必要があり、そうでないとショートする可能性がある。

STM32のI2Cに関する詳細情報と使用方法は以下の記事を参照:https://url.zeruns.com/JC0Ah

ここでは詳細は割愛する。

AHT10温湿度センサ

概要

AHT10は国産の温湿度センサチップで、価格が安く、精度が高く、体積も小さい。

AHT10は新設計のASIC専用チップ、改良されたMEMS半導体容量式湿度センシング素子、標準のオンチップ温度センシング素子を搭載しており、前世代センサを超える信頼性を実現。悪環境下でもより安定した性能を発揮する。

AHT10データシートダウンロード:https://url.zeruns.com/EDEwF


データシートを見ると大まかに以下の仕様がある:

  • 温度範囲:-40℃~85℃
  • 温度誤差:±0.3℃
  • 湿度範囲:0%~100%
  • 湿度誤差:±2%
  • 動作電圧:1.8V~3.6V
  • 通信方式:I2C
  • クロック周波数:100kHzおよび400kHz

以下のキー情報を見つける:

温湿度デバイスアドレスと読み書きコマンド

実際の使用では、AHT10のデバイスアドレスは読み書き方向ビットと1バイトに組み合わせて送信する。バイトの最下位が読み書き方向ビット、上位7ビットがAHT10のデバイスアドレス。

I2CでAHT10にデータまたはコマンドを書き込む場合、I2Cスタート信号の後に「0111 0000」すなわち0x70を送信する。上位7ビット「0111 000」でデバイスアドレスを指定し、最下位「0」で書き込み操作であることを通知。

AHT10からデータを読み出す場合、I2Cスタート信号の後に「0111 0001」すなわち0x71を送信する。上位7ビットでデバイスアドレスを指定し、最下位「1」で読み出し操作であることを通知。

簡単に言うと、0x70が書き込み、0x71が読み出し。ただしSTM32ハードウェアI2Cでは0x70のみ入力すればよく、最下位は標準ライブラリが処理してくれる。

温湿度データの読み出し


データシートより、1測定サイクルは3ステップ:

  1. 測定コマンド送信
  2. 測定完了待機
  3. 測定後データ読み出し

まとめると:

  1. 測定コマンド送信:書き込み命令(0x70)を送信後、トリガ測定命令(0xAC)とコマンドパラメータ(0x33と0x00)を送信。
  2. 測定完了待機:データシートには75msと記載、これ以上待てばよい。
  3. データ受信:読み出し命令(0x71)を送信し、6バイト連続で受信。最初のバイトはステータスバイトで、第3ビットのキャリブレーション有効ビットが1かチェック、1でなければ初期化コマンドを送信。第7ビットのビジー/アイドル指示をチェック、0なら測定完了として次へ。
  4. 受信データを変換処理。

データ計算

AHT10データシートより:

例:湿度値が0x0C6501で10進数に換算すると812289。
湿度 = 812289 × 100 / 1048576 = 77.46(単位:%)

温度値が0x056A00で10進数に換算すると354816。
温度 = (354816 × 200 / 1048576) − 50 = 17.67(単位:℃)

必要な部品

STM32最小システムボード:https://s.click.taobao.com/bqMwZRu

AHT10モジュール:https://s.click.taobao.com/gIF09Ru

OLEDモジュール:https://s.click.taobao.com/aNlvZRu

ジャンパワイヤ:https://s.click.taobao.com/xAkAJRu

ブレッドボード:https://s.click.taobao.com/ShJAJRu

ST-LINK V2:https://s.click.taobao.com/C8ftZRu

プログラム

ここではmain.c、AHT10.c、OLED.cの3主要ファイルのみ掲載、その他は下記リンクのZIPをダウンロード。

完全プロジェクトファイル:https://url.zeruns.com/AHT10

AHT10およびOLEDモジュールのSCL→PB6、SDA→PB7。

VSCodeでKeilの代わりにSTM32・51開発を行う方法:https://blog.zeruns.com/archives/690.html

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "IWDG.h"
#include "AHT10.h"

uint16_t numlen(uint16_t num);

int main(void)
{
	IWDG_Configuration();	// ウォッチドッグ初期化
	OLED_Init();			// OLEDディスプレイ初期化
	AHT10_Init();			// AHT10初期化
	
	OLED_ShowString(1, 1, "T:");
	OLED_ShowString(2, 1, "H:");

	uint32_t a=0;
	uint16_t err_count=0;
	
	while (1)
	{
		a++;
		OLED_ShowNum(3, 1, a, 9); // カウンタ表示(動作確認用)
		if(a==999999999)a=0;

		float Temp,Hum;				// 温湿度データ格納変数
		
		/*
		https://blog.zeruns.com
		*/

		if(ReadAHT10(&Hum,&Temp))	// 温湿度データ読み出し
		{
			if(Temp>=0)
			{
				char String[10];
				sprintf(String, "+%.2fC", Temp); // フォーマットして文字列へ
				OLED_ShowString(1, 3, String);	 // 温度表示

				sprintf(String, " %.2f%%", Hum);
				OLED_ShowString(2, 3, String);	 // 湿度表示
			}else
			{
				char String[10];
				sprintf(String, "-%.2fC", Temp);
				OLED_ShowString(1, 3, String);	 // 温度表示
				
				sprintf(String, " %.2f%%", Hum);
				OLED_ShowString(2, 3, String);	 // 湿度表示
			}
		}
		else
		{
			err_count++;
			OLED_ShowNum(4,1, err_count, 5); // エラー回数表示
		}

		Delay_ms(100);	// 100ms遅延

		IWDG_FeedDog();	// ウォッチドッグへの餌やり(1秒以上放置でリセット)
	}
}
```**AHT10.c**

```c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"

/* AHT10アドレス */
#define AHT10_ADDRESS 0x38<<1 // スレーブアドレスは7ビット、最後のビットは転送方向ビットなので1ビット左シフト

/* 使用するI2Cを設定 */
#define I2Cx I2C1

/*
https://blog.zeruns.com
*/

/* スタート信号を送信 */
void AHT10_I2C_START(){
    while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)); // バスがアイドルになるまで待機
    I2C_GenerateSTART(I2Cx, ENABLE); // スタート信号を送信
    while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR); // EV5イベントを検出
}

/* ストップ信号を送信 */
void AHT10_I2C_STOP(){
    I2C_GenerateSTOP(I2Cx, ENABLE); // ストップ信号を送信
}

/**
  * @brief  3バイトのデータを送信
  * @param  cmd コマンドバイト
  * @param  DATA0 第0パラメータ
  * @param  DATA1 第1パラメータ
  * @retval なし
  */
void AHT10_WriteByte(uint8_t cmd, uint8_t DATA0, uint8_t DATA1)
{
    AHT10_I2C_START();  // スタート信号を送信
    
    I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter);    // デバイス書き込みアドレスを送信
    while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  // EV6イベントを検出

    I2C_SendData(I2Cx, cmd); // コマンドを送信
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // EV8イベントを検出

    I2C_SendData(I2Cx, DATA0); // コマンドパラメータ上位8ビットデータを送信
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // EV8イベントを検出
   
    I2C_SendData(I2Cx, DATA1); // コマンドパラメータ下位8ビットデータを送信
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // EV8イベントを送信
    
    I2C_GenerateSTOP(I2Cx, ENABLE); // ストップ信号を送信    
}

/**
  * @brief  コマンドを送信してAHT10のステータスを読み取る
  * @retval 読み取ったステータスバイト
  */
/*uint8_t AHT10_ReadStatus(void){
    AHT10_I2C_START(); // スタート信号を送信  
    I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver); // デバイス読み取りアドレスを送信
    while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR); // EV6イベントを検出
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7イベントを検出
    I2C_AcknowledgeConfig(I2Cx, DISABLE); // ACK信号を無効化
    uint8_t status = I2C_ReceiveData(I2Cx); // データを読み取って返す
    AHT10_I2C_STOP();   // ストップ信号を送信
    I2C_AcknowledgeConfig(I2Cx,ENABLE); // ACK信号を再有効化
    return status;
}*/

/**
  * @brief  データを読み取る
  * @retval 読み取ったバイトデータ
  */
uint8_t AHT10_ReadData(void)
{
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7イベントを検出
    return I2C_ReceiveData(I2Cx); // データを読み取って返す
}

/* AHT10をソフトウェアリセット */
void AHT10_SoftReset(void)                    
{
    AHT10_I2C_START();  // スタート信号を送信
    I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter);    // デバイス書き込みアドレスを送信
    while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  // EV6イベントを検出
    I2C_SendData(I2Cx, 0xBA); // ソフトリセットコマンドを送信
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // EV8イベントを検出
    I2C_GenerateSTOP(I2Cx, ENABLE); // ストップ信号を送信
    Delay_ms(20);
}

/* ピン初期化 */
void AHT10_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); // I2C1クロックを有効化
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // GPIOBクロックを有効化
 
    /* STM32F103チップのハードウェアI2C1: PB6 -- SCL; PB7 -- SDA */
    GPIO_InitTypeDef  GPIO_InitStructure;               // GPIO設定用構造体を定義
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;     // 開ドレイン出力モードに設定、プルアップ抵抗が必要
    GPIO_Init(GPIOB, &GPIO_InitStructure);              // GPIOを初期化
    
    I2C_DeInit(I2Cx); // I2Cレジスタをデフォルト値にリセット
    I2C_InitTypeDef  I2C_InitStructure;                 // I2C設定用構造体を定義
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;          // 動作モード
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;  // クロックデューティ比、Tlow/Thigh = 2
    I2C_InitStructure.I2C_OwnAddress1 = 0x88; // マスターのI2Cアドレス、使用しない場合は適当な値で問題なし
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; // ACKビットを有効化
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // 7ビットアドレス長を設定
    I2C_InitStructure.I2C_ClockSpeed = 400000; // I2C転送速度、400K、使用チップのデータシートでサポート速度を確認
    I2C_Init(I2Cx, &I2C_InitStructure);         // I2Cを初期化

    I2C_Cmd(I2Cx, ENABLE);  // I2Cを有効化
    Delay_ms(20); // パワーオン遅延
    AHT10_WriteByte(0XE1,0X08,0x00); // 初期化コマンドを送信
    Delay_ms(20);
}

/**
  * @brief  AHT10データを読み取る
  * @param  *Hum 湿度
  * @param  *Temp 温度
  * @retval 1 - 読み取り成功;0 - 読み取り失敗
  */
uint8_t ReadAHT10(float *Hum,float *Temp)
{
    uint8_t Data[5]; // 読み取ったデータを格納する変数を宣言

    AHT10_WriteByte(0XAC,0X33,0x00); // 測定トリガコマンドを送信

    Delay_ms(70); // 測定完了を待つために70ミリ秒遅延

    AHT10_I2C_START(); // スタート信号を送信  
    I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver); // デバイス読み取りアドレスを送信
    while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR); // EV6イベントを検出
    
    uint8_t i;
    for(i=0;i<6;i++) // 6バイトデータを6回ループで読み取る
    {
        if (i == 5) // 最後の1バイト読み取り時にACK信号を無効化
        {
            I2C_AcknowledgeConfig(I2Cx, DISABLE); // ACK信号を無効化
        }
        Data[i] = AHT10_ReadData(); // データを読み取る
        if (i == 5)
            I2C_GenerateSTOP(I2Cx, ENABLE); // ストップ信号を送信
    }
    I2C_AcknowledgeConfig(I2Cx,ENABLE); // ACK信号を再有効化

    if( (Data[0]&0x08) == 0 ) // 0x08(00001000) ステータスバイトのビット3(キャリブレーション有効ビット)が0かチェック
    {
        AHT10_WriteByte(0XE1,0X08,0x00); // 初期化コマンドを送信
        Delay_ms(20);
        return 0;
    }
    else if( (Data[0]&0x80) == 0 ) // 0x80(10000000) ステータスバイトのビット7(ビジー/アイドル指示)が0かチェック
    {
        
        uint32_t SRH = (Data[1]<<12) | (Data[2]<<4) | (Data[3]>>4); // 湿度データ処理
        uint32_t ST = ((Data[3]&0x0f)<<16) | (Data[4]<<8) | Data[5]; // 温度データ処理

        *Hum = (SRH * 100.0) / 1024.0 / 1024;   // データシートの公式に従って湿度データを変換
        *Temp = (ST * 200.0) / 1024.0 / 1024 - 50; // データシートの公式に従って温度データを変換

        return 1;
    }

    I2C_GenerateSTOP(I2Cx, ENABLE); // ストップ信号を送信
    return 0;    
}

/*
https://blog.zeruns.com
*/
```**OLED.c**

```c
#include "stm32f10x.h"
#include "OLED_Font.h"

/*OLED画面アドレス*/
#define OLED_ADDRESS 0x78

/*どのI2Cを使うか*/
#define I2Cx I2C1

/*
https://blog.zeruns.com
*/

/*ピン初期化*/
void OLED_I2C_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//I2C1クロックを有効化
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//GPIOBクロックを有効化
 
	/*STM32F103チップのハードウェアI2C: PB6 -- SCL; PB7 -- SDA */
	GPIO_InitTypeDef  GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;		//出力モードをオープンドレインに設定、プルアップ抵抗が必要
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_DeInit(I2Cx);	//I2C周辺レジスタをデフォルト値にリセット
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//動作モード
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//クロックデューティ比、Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x88;	//マスターのI2Cアドレス、使わない場合は適当でOK
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//応答ビットを有効化
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//アドレス長を7ビットに設定
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//I2C転送速度400K、使用チップのデータシートでサポート速度を確認すること。	
	I2C_Init(I2Cx, &I2C_InitStructure);

	I2C_Cmd(I2Cx, ENABLE);
}

void I2C_WriteByte(uint8_t addr,uint8_t data)
{
	
	while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
	
	//スタート条件を送信
	I2C_GenerateSTART(I2Cx, ENABLE);
	//EV5イベントを検出
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
	//スレーブ書き込みアドレスを送信
	I2C_Send7bitAddress(I2Cx, OLED_ADDRESS, I2C_Direction_Transmitter);
	//EV6イベントを検出
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
	
	//デバイス内部アドレスを送信
	I2C_SendData(I2Cx, addr);
	//EV8_2イベントを検出
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_SendData(I2Cx, data);//データ送信
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

	//ストップ条件を送信
	I2C_GenerateSTOP(I2Cx, ENABLE);
}
 
/**
  * @brief  OLEDコマンド書き込み
  * @param  Command 書き込むコマンド
  * @retval なし
  */
void OLED_WriteCommand(unsigned char Command)//コマンド書き込み
{
	I2C_WriteByte(0x00, Command);
}
 
/**
  * @brief  OLEDデータ書き込み
  * @param  Data 書き込むデータ
  * @retval なし
*/
void OLED_WriteData(unsigned char Data)//データ書き込み
{
	I2C_WriteByte(0x40, Data);
}

/**
  * @brief  OLEDカーソル位置設定
  * @param  Y 左上原点、下方向の座標、範囲:0~7
  * @param  X 左上原点、右方向の座標、範囲:0~127
  * @retval なし
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//Y位置設定
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//X位置下位4ビット設定
	OLED_WriteCommand(0x00 | (X & 0x0F));			//X位置上位4ビット設定
}

/**
  * @brief  OLED画面クリア
  * @param  なし
  * @retval なし
  */
void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

/**
  * @brief  OLED部分クリア
  * @param  Line 行位置、範囲:1~4
  * @param  start 列開始位置、範囲:1~16
  * @param  end 列終了位置、範囲:1~16
  * @retval なし
  */
void OLED_Clear_Part(uint8_t Line, uint8_t start, uint8_t end)
{  
	uint8_t i,Column;
	for(Column = start; Column <= end; Column++)
	{
		OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//上半分にカーソル位置設定
		for (i = 0; i < 8; i++)
		{
			OLED_WriteData(0x00);			//上半分表示
		}
		OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//下半分にカーソル位置設定
		for (i = 0; i < 8; i++)
		{
			OLED_WriteData(0x00);		//下半分表示
		}
	}
}

/**
  * @brief  OLED1文字表示
  * @param  Line 行位置、範囲:1~4
  * @param  Column 列位置、範囲:1~16
  * @param  Char 表示する1文字、範囲:ASCII表示可能文字
  * @retval なし
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//上半分にカーソル位置設定
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//上半分表示
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//下半分にカーソル位置設定
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//下半分表示
	}
}

/**
  * @brief  OLED文字列表示
  * @param  Line 開始行位置、範囲:1~4
  * @param  Column 開始列位置、範囲:1~16
  * @param  String 表示する文字列、範囲:ASCII表示可能文字
  * @retval なし
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED累乗関数
  * @retval XのY乗を返す
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED数値表示(10進、正数)
  * @param  Line 開始行位置、範囲:1~4
  * @param  Column 開始列位置、範囲:1~16
  * @param  Number 表示する数値、範囲:0~4294967295
  * @param  Length 表示する桁数、範囲:1~10
  * @retval なし
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED数値表示(10進、符号付き)
  * @param  Line 開始行位置、範囲:1~4
  * @param  Column 開始列位置、範囲:1~16
  * @param  Number 表示する数値、範囲:-2147483648~2147483647
  * @param  Length 表示する桁数、範囲:1~10
  * @retval なし
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED数値表示(16進、正数)
  * @param  Line 開始行位置、範囲:1~4
  * @param  Column 開始列位置、範囲:1~16
  * @param  Number 表示する数値、範囲:0~0xFFFFFFFF
  * @param  Length 表示する桁数、範囲:1~8
  * @retval なし
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)							
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED数値表示(2進、正数)
  * @param  Line 開始行位置、範囲:1~4
  * @param  Column 開始列位置、範囲:1~16
  * @param  Number 表示する数値、範囲:0~1111 1111 1111 1111
  * @param  Length 表示する桁数、範囲:1~16
  * @retval なし
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

/**
  * @brief  OLED初期化
  * @param  なし
  * @retval なし
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//電源投入後ディレイ
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//ポート初期化
	
	OLED_WriteCommand(0xAE);	//ディスプレイOFF
	
	OLED_WriteCommand(0xD5);	//表示クロック分周比/発振周波数設定
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//マルチプレックス比設定
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//表示オフセット設定
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//表示開始行設定
	
	OLED_WriteCommand(0xA1);	//左右反転設定、0xA1通常 0xA0左右反転
	
	OLED_WriteCommand(0xC8);	//上下反転設定、0xC8通常 0xC0上下反転

	OLED_WriteCommand(0xDA);	//COMピンハードウェア設定
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//コントラスト設定
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//プリチャージ期間設定
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//VCOMH選択レベル設定
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//全体表示ON/OFF設定

	OLED_WriteCommand(0xA6);	//正/反転表示設定

	OLED_WriteCommand(0x8D);	//チャージポンプ設定
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//ディスプレイON
		
	OLED_Clear();				//OLED画面クリア
}

おすすめ記事

「いいね!」 1