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ステップ:
- 測定コマンド送信
- 測定完了待機
- 測定後データ読み出し
まとめると:
- 測定コマンド送信:書き込み命令(0x70)を送信後、トリガ測定命令(0xAC)とコマンドパラメータ(0x33と0x00)を送信。
- 測定完了待機:データシートには75msと記載、これ以上待てばよい。
- データ受信:読み出し命令(0x71)を送信し、6バイト連続で受信。最初のバイトはステータスバイトで、第3ビットのキャリブレーション有効ビットが1かチェック、1でなければ初期化コマンドを送信。第7ビットのビジー/アイドル指示をチェック、0なら測定完了として次へ。
- 受信データを変換処理。
データ計算
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画面クリア
}
おすすめ記事
- 高コスパで安価なVPS/クラウドサーバーおすすめ: https://blog.vpszj.cn/archives/41.html
- NPSで内網貫通サーバーを構築、Webパネル付き:https://blog.vpszj.cn/archives/748.html
- LinuxでWebサイトを構築するチュートリアル:https://blog.vpszj.cn/archives/1094.html
- マイクラサーバー構築チュートリアル:https://blog.vpszj.cn/archives/tag/minecraft
- STM32とHC-SR04モジュールで超音波距離測定機能を実装:https://blog.zeruns.com/archives/680.html
- ESP8266開発環境の構築とプロジェクトデモ:https://blog.zeruns.com/archives/526.html








