STM32でSHT3xシリーズ(SHT30、SHT31、SHT35)温湿度センサーのデータを読み取り、0.96インチOLEDスクリーンに表示します。
以下に2つのコードを提供します。1つは標準ライブラリでハードウェアI2Cを使用したもの、もう1つはHALライブラリでソフトウェアシミュレーションI2Cを使用したものです。
使用しているマイコンはSTM32F103C8T6、温湿度センサーはSHT30です。
STM32ソフトウェアI2CでAM2320温湿度センサーデータを読み取る:https://blog.zeruns.com/archives/695.html
STM32ハードウェアI2CでSHTC3温湿度センサーを読み取る:https://blog.zeruns.com/archives/692.html
STM32マイコンでAHT10温湿度センサーデータを読み取る:https://blog.zeruns.com/archives/693.html
電子/マイコン技術交流グループ:2169025065
実装効果図
I2Cプロトコルの概要
I2C通信プロトコル(Inter-Integrated Circuit)はPhilips社によって開発されました。ピン数が少なく、ハードウェア実装が簡単で、拡張性が強く、USART、CANなどの通信プロトコルの外部送受信デバイス(レベル変換チップ)が不要なため、現在、システム内の複数の集積回路(IC)間の通信に広く使用されています。
I2Cには1本のデータバスSDA(Serial Data Line)、シリアルデータバスのみがあり、データを1ビットずつ送信することができ、シリアル通信に属し、半二重通信を採用しています。
半二重通信:双方向通信を実現できますが、2つの方向で同時に実行することはできず、交互に実行する必要があります。実際には、方向を切り替えることができる単工通信と理解することもできます。同時に1つの方向のみ送信でき、1本のデータラインのみが必要です。
I2C通信プロトコルについて、物理層とプロトコル層に分けます。物理層は通信システムの機械的および電子的機能部分の特性(ハードウェア部分)を規定し、物理メディア上の元のデータの送信を保証します。プロトコル層は主に通信ロジックを規定し、送受信側のデータパッケージング、アンパッケージング標準(ソフトウェアレベル)を統一します。
I2C物理層
I2C通信デバイス間の一般的な接続方法
(1) これは複数デバイスをサポートするバスです。「バス」は複数のデバイスが共有する信号線を指します。1つのI2C通信バスでは、複数のI2C通信デバイスを接続でき、複数の通信マスターと複数の通信スレーブをサポートします。
(2) 1つのI2Cバスは2本のバスラインのみを使用します。1本は双方向シリアルデータラインSDA(Serial Data Line)、1本はシリアルクロックラインSCL(Serial Clock Line)です。データラインはデータを表すために使用され、クロックラインはデータ送受信の同期に使用されます。
(3) バスは上拉抵抗を通じて電源に接続されます。I2Cデバイスがアイドル状態の場合、高インピーダンス状態を出力します。すべてのデバイスがアイドル状態で高インピーダンス状態を出力する場合、上拉抵抗がバスを高電位に引き上げます。
I2C通信時、マイコンのGPIOポートはオープンドレイン出力に設定する必要があります。そうしないと短絡が発生する可能性があります。
STM32のI2C関連情報と使用方法の詳細については、こちらの記事を参照してください:https://url.zeruns.com/JC0Ah
ここでは詳しく説明しません。
SHT3x温湿度センサー
SHT3xデータシートダウンロードアドレス:
オリジナル版:https://url.zeruns.com/uWNnP 抽出コード:icqu
中国語版(機械翻訳):https://url.zeruns.com/n9b9t 抽出コード:vedy
データシートを参照することで、SHT30は温度と湿度を検出できるセンサーであることが分かります。
温度範囲:-40℃~125℃
湿度範囲:0%~100%
動作電圧:2.4V~5.5V
通信方式:I2C
クロック周波数:0~1000kHz
以下の重要な情報が見つかります。
SHT3xデバイスアドレスと読み書きコマンド
SHT3xのアドレスは、第2ピンに高または低電位を接続することで設定できます。淘宝で購入したモジュールのセンサー2ピンは抵抗を通じてVCCに接続されているため、デフォルトは0x44です。
実際の使用では、SHT3xのデバイスアドレスは読み書きデータ/コマンド方向ビットと組み合わせて1バイトを形成し、同時に送信する必要があります。バイトの最下位ビットは読み書きデータ/コマンド方向ビットで、上位7ビットはSHT3xのデバイスアドレスです。
I2Cを通じてSHT3xにデータまたはコマンドを書き込む場合、I2C開始信号の後、「1000 1000」、つまり0x88をSHT3xに送信する必要があります。上位7ビット「1000 100」(0x44)のデバイスアドレスでアドレス指定するだけでなく、最下位ビット「0」を通じてSHT3xに次のデータまたはコマンド操作を書き込むことを通知します。
I2Cを通じてSHT3xからデータを読み取る場合、I2C開始信号の後、「1000 1001」、つまり0x89をSHT3xに送信する必要があります。上位7ビット「1000 100」のデバイスアドレスでアドレス指定するだけでなく、最下位ビット「1」を通じてSHT3xに次のデータ読み取り操作を通知します。
簡単に言えば、0x88はデータ書き込みを表し、0x89はデータ読み取りを表します。ただし、STM32ハードウェアI2Cを使用する場合は、0x88を入力するだけで、最下位ビットは標準ライブラリが処理します。
温湿度データの読み取り
異なるコマンドでは、データ取得方法が異なり、単一測定と周期測定モード、およびClock Stretching EnableとDisableの違いがあります。
Clock Stretchingはクロック拡張を意味します。Clock Stretching Enableコマンドを使用する場合、測定コマンド送信後、SHT3xが温度湿度データを測定している間、SHT3xはI2CのクロックラインSCLを低くします。これにより、マスターがSHT3xにコマンドを送信することが禁止されます。SHT3xが温度湿度データ測定を完了した場合のみ、SHT3xはクロックラインSCLを解放します。
Clock Stretching Disableコマンドを使用する場合、SHT3xがデータを測定している間、SHT3xはI2CのクロックラインSCLを低くしません。ただし、SHT3xがデータを測定している間にマスターがコマンドまたはデータを送信する場合、読み取り指令を送信するとSHT3xは非応答信号を返します。測定完了後に読み取り指令を送信した場合のみ、応答信号が返されます。
周期測定モードにより、センサーは自動的に測定してデータを保存できます。1秒あたり0.5/1/2/4/10回の測定を設定でき、読み取り指令0xE000を通じて最新の測定結果を読み取ることができます。
データシートから、1つの測定サイクルは2つのステップで構成されることが分かります:
- 測定コマンドを送信する
- 測定完了後のデータを読み取る。
上記の測定コマンドと読み取りコマンドはデータシートで確認できます。
以下にまとめます:
- 測定コマンドを送信:まず書き込み指令(0x88)を送信し、次に起動指令上位(0x2C)を送信し、次に起動指令下位(0x0D)を送信します。
- データを読み取り、測定完了を待つ:読み取り指令(0x89)を送信し、スレーブがSCLバスを解放するのを待ちます。
- データを受信:連続して6バイトのデータを受信します。これら6バイトの第1~2バイトは温度値、第3バイトは温度チェックサムです。第4~5バイトは湿度値、第6バイトは湿度チェックサムです。最後のバイト受信後、非応答信号を送信します。
- データ処理:データに対してCRCチェックを実行し、処理します。
データの計算
SHT3xデータシートから
例えば、採集された湿度値が0x6501の場合、10進数に変換すると25857です。
湿度 = 100 * 25857 / (65536 - 1) = 39.45 (単位:%)
採集された温度値が0x6600の場合、10進数に変換すると26112です。
温度 = -45 + 175 * 26112 / (65536 - 1) = 24.72 (単位:℃)
必要な部品
STM32最小システムボード:https://s.click.taobao.com/AJEGiNu
SHT3xモジュール:https://s.click.taobao.com/55xGiNu
OLEDモジュール:https://s.click.taobao.com/0dlG0Ou
デュポンワイヤ:https://s.click.taobao.com/xAkAJRu
ブレッドボード:https://s.click.taobao.com/ShJAJRu
ST-LINK V2:https://s.click.taobao.com/C8ftZRu
プログラム
ここでは標準ライブラリ版のmain.c、SHT3x.c、oled.cの3つの主要なコードを掲載しています。その他については、以下のリンクの圧縮パッケージをダウンロードしてください。
標準ライブラリ版:https://url.zeruns.com/a49EX 抽出コード:8nn5
HALライブラリ版:https://url.zeruns.com/p3og4 抽出コード:v9wc
SHT3xと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 "SHT3x.h"
uint16_t numlen(uint16_t num);
int main(void)
{
IWDG_Configuration(); //ウォッチドッグを初期化
OLED_Init(); //OLEDスクリーンを初期化
SHT3x_Init(); //SHT3xを初期化
/*OLED_ShowString(1, 1, "T:");
OLED_ShowString(3, 1, "H:");*/
uint8_t Chinese[]={0,1};
OLED_ShowChinese(1,1,Chinese,2);//温度
uint8_t Chinese1[]={2,3};
OLED_ShowChinese(3,1,Chinese1,2);//湿度
uint8_t Chinese2[]={4};
OLED_ShowChinese(1,11,Chinese2,1);//℃
uint32_t a=0;
uint16_t err_count=0;
while (1)
{
a++;
OLED_ShowNum(2, 14, a, 2);
if(a==99)a=0;
float Temp,Hum; //温湿度データを格納する変数を宣言
``````c
if(ReadSHT3x(&Hum,&Temp)) //温湿度データを読み取る
{
if(Temp>=0)
{
char String[10];
sprintf(String, "+%.2f", Temp);//文字列をフォーマットして文字列変数に出力
OLED_ShowString(1, 5, String); //温度を表示
sprintf(String, " %.2f%%", Hum); //文字列をフォーマットして文字列変数に出力
OLED_ShowString(3, 5, String); //湿度を表示
}else
{
char String[10];
sprintf(String, "%.2f", Temp);//文字列をフォーマットして文字列変数に出力
OLED_ShowString(1, 5, String); //温度を表示
sprintf(String, " %.2f%%", Hum); //文字列をフォーマットして文字列変数に出力
OLED_ShowString(3, 5, String); //湿度を表示
}
}
else
{
err_count++;
OLED_ShowNum(1,14, err_count, 2); //エラーカウント数を表示
}
/*
https://blog.zeruns.com
*/
Delay_ms(200); //200ミリ秒遅延
IWDG_FeedDog(); //ウォッチドッグに給餌(1秒以上給餌しないと自動的にリセット)
}
}
/**
* @brief 整数の長さを計算
* @param num 長さを計算する整数
* @retval 長さの値
*/
uint16_t numlen(uint16_t num)
{
uint16_t len = 0; // 初期長さは0
for(; num > 0; ++len) // numが0より大きいかどうかを判定し、そうでなければ長さ+1
num /= 10; // 除算を使用して計算し、numが1未満になるまで続ける
return len; // 長さの値を返す
}
SHT3x.c
#include "stm32f10x.h"
#include "Delay.h"
/*SHT3xアドレス*/
#define SHT3x_ADDRESS 0x44<<1 //スレーブアドレスは7ビットなので1ビット左シフト
/*使用するI2Cを設定*/
#define I2Cx I2C1
/*
https://blog.zeruns.com
*/
/**
* @brief CRCチェック、CRC多項式:x^8+x^5+x^4+1、つまり0x31
* @param DAT チェックするデータ
* @retval チェックコード
*/
uint8_t SHT3x_CRC_CAL(uint16_t DAT)
{
uint8_t i,t,temp;
uint8_t CRC_BYTE;
CRC_BYTE = 0xFF;
temp = (DAT>>8) & 0xFF;
for(t = 0; t < 2; t++)
{
CRC_BYTE ^= temp;
for(i = 0;i < 8;i ++)
{
if(CRC_BYTE & 0x80)
{
CRC_BYTE <<= 1;
CRC_BYTE ^= 0x31;
}
else
{
CRC_BYTE <<= 1;
}
}
if(t == 0)
{
temp = DAT & 0xFF;
}
}
return CRC_BYTE;
}
/*スタート信号を送信*/
void SHT3x_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 SHT3x_I2C_STOP(){
I2C_GenerateSTOP(I2Cx, ENABLE);//ストップ信号を送信
}
/**
* @brief 2バイトのデータを送信
* @param MSB 上位8ビット
* @param LSB 下位8ビット
* @retval なし
*/
void SHT3x_WriteByte(uint8_t MSB,uint8_t LSB)
{
SHT3x_I2C_START(); //スタート信号を送信
I2C_Send7bitAddress(I2Cx, SHT3x_ADDRESS, I2C_Direction_Transmitter); //デバイス書き込みアドレスを送信
while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //EV6イベントを検出
I2C_SendData(I2Cx, MSB);//上位8ビットデータを送信
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8イベントを検出
I2C_SendData(I2Cx, LSB);//下位8ビットデータを送信
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8イベントを検出
I2C_GenerateSTOP(I2Cx, ENABLE);//ストップ信号を送信
}
/**
* @brief データを読み取る
* @retval 読み取ったバイトデータ
*/
uint8_t SHT3x_ReadData()
{
while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//EV7イベントを検出
return I2C_ReceiveData(I2Cx);//データを読み取り、返す
}
/*SHT3xをソフトウェアリセット*/
void SHT3x_SoftReset(void)
{
SHT3x_WriteByte(0x30,0xA2); //SHT3xを再初期化
}
/*ピン初期化*/
void SHT3x_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 = 0x30; //マスターのI2Cアドレス、使用しない場合は適当に書いても影響なし
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を初期化
I2C_Cmd(I2Cx, ENABLE); //I2Cを有効化
//SHT3x_WriteByte(0X27,0X21); //周期データ取得モード(毎秒10回、Medium Repeatability)
Delay_us(300);//300マイクロ秒遅延
}
/**
* @brief SHT3xデータを読み取る
* @param *Hum 湿度
* @param *Temp 温度
* @retval 1 - 読み取り成功;0 - 読み取り失敗
*/
uint8_t ReadSHT3x(float *Hum,float *Temp)
{
uint16_t HumData,TempData,HumCRC,TempCRC;//読み取ったデータを格納する変数を宣言
//SHT3x_WriteByte(0XE0,0X00); //命令を送信し、データを取得、周期データ取得モード用
SHT3x_WriteByte(0X2C,0X0D); //単一測定命令を送信(クロック延伸を開始、Medium Repeatability)
SHT3x_I2C_START();//スタート信号を送信
I2C_Send7bitAddress(I2Cx,SHT3x_ADDRESS,I2C_Direction_Receiver);//デバイス読み取りアドレスを送信
while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//EV6イベントを検出
TempData = SHT3x_ReadData();//温度上位8ビットデータを読み取る
TempData=TempData<<8; //8ビット左シフト
TempData |= SHT3x_ReadData();//温度下位8ビットデータを読み取る
TempCRC = SHT3x_ReadData(); //温度CRCチェックデータを読み取る
HumData = SHT3x_ReadData(); //湿度上位8ビットデータを読み取る
HumData=HumData<<8; //8ビット左シフト
HumData |= SHT3x_ReadData();//湿度下位8ビットデータを読み取る
I2C_AcknowledgeConfig(I2Cx,DISABLE); //応答信号を無効化
HumCRC = SHT3x_ReadData(); //湿度CRCチェックデータを読み取る
SHT3x_I2C_STOP(); //ストップ信号を送信
I2C_AcknowledgeConfig(I2Cx,ENABLE); //応答信号を有効化
if( SHT3x_CRC_CAL(HumData)==HumCRC && SHT3x_CRC_CAL(TempData)==TempCRC ){ //受信したデータに対してCRCチェックを実行
*Hum = (float)HumData*100/(65536-1); //受信した16ビットバイナリデータを10進湿度データに変換
*Temp = (float)TempData*175/(65536-1)-45; //受信した16ビットバイナリデータを10進温度データに変換
return 1;
}
return 0;
}
oled.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 = 0x30; //マスターのI2Cアドレス、使用しない場合は適当に書いても影響なし
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);
}
}
}
``````c
/**
* @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 OLED1つの中国語文字を表示
* @param Line 行位置、範囲:1~4
* @param Column 列位置、範囲:1~16
* @param Chinese 表示する中国語文字がフォントライブラリ配列内の位置
* @retval なし
*/
void OLED_ShowWord(uint8_t Line, uint8_t Column, uint8_t Chinese)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F16x16[Chinese][i]);
}
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8 + 8);
for (i = 1; i < 8; i++)
{
OLED_WriteData(OLED_F16x16[Chinese][i+8]);
}
OLED_SetCursor((Line - 1) * 2 +1, (Column - 1) * 8);
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F16x16[Chinese][i+16]);
}
OLED_SetCursor((Line - 1) * 2 +1, (Column - 1) * 8 + 8);
for (i = 1; i < 8; i++)
{
OLED_WriteData(OLED_F16x16[Chinese][i+16+8]);
}
}
/**
* @brief OLED複数の中国語文字を表示
* @param Line 行位置、範囲:1~4
* @param Column 列位置、範囲:1~16
* @param Chinese[] 表示する中国語文字がフォントライブラリ配列内の位置、配列内に各文字の位置を配置
* @param Length 表示する中国語の長さ、範囲:1~8
* @retval なし
*/
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t *Chinese,uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowWord(Line, Column + i*2,Chinese[i]);
}
}
/**
* @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); //ディスプレイをオフ
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); //全ディスプレイオン/オフを設定
OLED_WriteCommand(0xA6); //正常/反転表示を設定
OLED_WriteCommand(0x8D); //チャージポンプを設定
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //ディスプレイをオン
OLED_Clear(); //OLEDクリア
}
推奨読み物
- 高コストパフォーマンスと安いVPS/クラウドサーバーの推奨: https://blog.vpszj.cn/archives/41.html
- NPSを使用した内部ネットワーク貫通サーバーの構築、Webパネル付き:https://blog.vpszj.cn/archives/748.html
- Linuxウェブサイト構築チュートリアル、サイト構築チュートリアル: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






