STM32でハードウェアI2Cを使ってSHTC3温湿度センサからデータを読み取り、0.96インチOLEDディスプレイに表示する。
使用したのはSTM32F103C8T6で、プログラムはST標準ライブラリで書いている。
電子/マイコン技術交流グループ:2169025065
実装効果図
I2Cプロトコル概要
I2C通信プロトコル(Inter-Integrated Circuit)はPhilips社によって開発されたもので、ピン数が少なく、ハードウェアの実装が簡単で、拡張性が高く、USARTやCANなどの通信プロトコルのような外部送受信デバイス(レベル変換チップ)が不要なため、現在ではシステム内の複数の集積回路(IC)間の通信で広く使用されている。
I2Cにはデータバスが1本だけで、SDA(Serial Data Line)と呼ばれるシリアルデータバスであり、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
ここでは詳細は説明しない。
SHTC3温湿度センサ
SHTC3データシートダウンロード先:https://url.zeruns.com/WpLDy
データシートを見ると、SHTC3は温度と湿度を検出できるセンサであることがわかる。
温度範囲:-40℃~125℃
湿度範囲:0%~100%
動作電圧:1.6V~3.6V
通信方式:I2C
クロック周波数:3つのモードがあり、それぞれ0~100kHz、0~400kHz、0~1000kHz
以下の重要な情報を見つける:
温湿度デバイスアドレスと読み書きコマンド
実際の使用では、SHTC3のデバイスアドレスは読み書きデータ/コマンド方向ビットと組み合わせて1バイトとして同時に送信する必要がある。バイトの最下位ビットが読み書きデータ/コマンド方向ビットで、上位7ビットがSHTC3のデバイスアドレスである。
I2Cを介してSHTC3にデータまたはコマンドを書き込む場合、I2Cスタート信号の後に「1110 0000」、つまり0xE0をSHTC3に送信する必要がある。上位7ビット「1110 000」のデバイスアドレスでアドレッシングし、最下位ビット「0」で次は書き込み操作であることをSHTC3に通知する。
I2Cを介してSHTC3からデータを読み取る場合、I2Cスタート信号の後に「1110 0001」、つまり0xE1をSHTC3に送信する必要がある。上位7ビット「1110 000」のデバイスアドレスでアドレッシングし、最下位ビット「1」で次は読み取り操作であることをSHTC3に通知する。
簡単に言うと、0xE0はデータ書き込み、0xE1はデータ読み取りを表す。ただし、STM32のハードウェアI2Cを使用する場合は0xE0のみを入力すればよく、最下位ビットは標準ライブラリが処理する。
温湿度データの読み取り
わかることは、異なるコマンドでは取得するデータの順序が異なるだけでなく、Clock Stretching EnableとDisableの違いもある。
Clock Stretchingはクロックストレッチの意味。Clock Stretching Enableコマンドを使用する場合、測定コマンドを送信した後、SHTC3が温湿度データを測定している間、SHTC3はI2CのクロックラインSCLをプルダウンし、これによってホストがSHTC3にコマンドを送信することを禁止し、SHTC3が温湿度データの測定を完了したときにのみSCLを開放する。
Clock Stretching Disableコマンドを使用する場合、SHTC3がデータを測定している間はSCLをプルダウンせず、測定中にホストがコマンドまたはデータを送信してもSHTC3は応答しない。ホストはSHTC3からの応答信号の有無で測定が完了したかどうかを判断できる。
データシートによると、1つの測定サイクルは4つのステップに分かれる:
- ウェイクアップコマンドを送信
- 測定コマンドを送信
- 測定完了後のデータを読み取る
- スリープコマンドを送信
ウェイクアップコマンドとスリープコマンドはデータシートで確認できる:
まとめ:
- SHTC3をウェイクアップ:書き込み命令(0xE0)を送信し、次にウェイクアップ命令上位(0x35)、ウェイクアップ命令下位(0x17)を送信
- ウェイクアップ待機:データシートによる最大ウェイクアップ時間は240μsで、これ以上待てばよい
- 収集命令を送信:書き込み命令(0xE0)を送信し、次に収集命令の上位と下位を送信。収集命令は複数あり、必要に応じて選択
- データ受信:読み取り命令(0xE1)を送信し、6バイトのデータを連続で受信。収集命令が温度優先の場合、6バイトのうち1~2バイト目が温度値、3バイト目が温度チェックサム、4~5バイト目が湿度値、6バイト目が湿度チェックサム。湿度優先の場合は前後3バイトが逆になる
- スリープ移行:書き込み命令を送信し、スリープ命令を送信してスリープ状態に入る
データの計算
SHTC3データシートによると:
例:収集した湿度値が0x6501で、10進数に変換すると25857
則:湿度 = 100 × 25857 / 65536 = 39.45(単位:%)
収集した温度値が0x6600で、10進数に変換すると26112
則:温度 = -45 + 175 × 26112 / 65536 = 24.72(単位:℃)
必要な部品
STM32最小システムボード:https://s.click.taobao.com/bqMwZRu
SHTC3モジュール:https://s.click.taobao.com/WxACJRu
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、shtc3.c、oled.cの3つの主要なコードのみを掲載する。他は以下のリンクからダウンロードしてほしい。
完全なプロジェクトファイル:https://url.zeruns.com/EXCvo
SHTC3および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 "SHTC3.h"
uint16_t numlen(uint16_t num);
int main(void)
{
IWDG_Configuration(); //ウォッチドッグ初期化
OLED_Init(); //OLEDディスプレイ初期化
SHTC3_I2C_Init(); //SHTC3初期化
OLED_ShowString(1, 1, "T:");
OLED_ShowString(2, 1, "H:");
OLED_ShowString(4, 1, "err_count:");
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; //温湿度データを格納する変数を宣言
if(ReadSHTC3(&Hum,&Temp)) //温湿度データを読み取る
{
if(Temp>=0)
{
char String[10];
sprintf(String, "%.2fC", Temp); //フォーマットされた文字列を文字列変数に出力
OLED_ShowString(1, 3, String); //温度を表示
/*
OLED_ShowNum(1,3, (uint8_t)Temp, numlen((uint8_t)Temp));//温度整数部を表示
OLED_ShowChar(1, 3+numlen((uint8_t)Temp), '.'); //小数点を表示
OLED_ShowNum(1,3+numlen((uint8_t)Temp)+1, (uint8_t)(Temp*100)%100, 2); //温度小数部を表示
OLED_ShowChar(1, 3+numlen((uint8_t)Temp)+1+2, 'C'); //記号を表示
*/
```sprintf(String, \"%.2f%%\", Hum); // 文字列をフォーマットして文字列変数に出力
OLED_ShowString(2, 3, String); // 湿度を表示
/*
OLED_ShowNum(2,3, (uint8_t)Hum, numlen((uint8_t)Hum)); // 湿度の整数部を表示
OLED_ShowChar(2, 3+numlen((uint8_t)Hum), '.'); // 小数点を表示
OLED_ShowNum(2,3+numlen((uint8_t)Hum)+1, (uint8_t)(Hum*100)%100, 2); // 湿度の小数部を表示
OLED_ShowChar(2, 3+numlen((uint8_t)Hum)+1+2, '%'); // 記号を表示
*/
}else
{
char String[10];
sprintf(String, "-%.2fC", Temp);//文字列をフォーマットして文字列変数に出力
OLED_ShowString(1, 3, String); // 温度を表示
/*
OLED_ShowChar(1, 3, '-'); // 負号を表示
OLED_ShowNum(1,3+1, (uint8_t)Temp, numlen((uint8_t)Temp)); // 温度の整数部を表示
OLED_ShowChar(1, 3+1+numlen((uint8_t)Temp), '.'); // 小数点を表示
OLED_ShowNum(1,3+1+numlen((uint8_t)Temp)+1, (uint8_t)(Temp*100)%100, 2); // 温度の小数部を表示
OLED_ShowChar(1, 3+1+numlen((uint8_t)Temp)+1+2, 'C'); // 記号を表示
*/
sprintf(String, \"%.2f%%\", Hum); // 文字列をフォーマットして文字列変数に出力
OLED_ShowString(2, 3, String); // 湿度を表示
/*
OLED_ShowNum(2,3, (uint8_t)Hum, numlen((uint8_t)Hum)); // 湿度の整数部を表示
OLED_ShowChar(2, 3+numlen((uint8_t)Hum), '.'); // 小数点を表示
OLED_ShowNum(2,3+numlen((uint8_t)Hum)+1, (uint8_t)(Hum*100)%100, 2); // 湿度の小数部を表示
OLED_ShowChar(2, 3+numlen((uint8_t)Hum)+1+2, '%'); // 記号を表示
*/
}
}
else
{
err_count++;
OLED_ShowNum(4,11, err_count, numlen(err_count)); // エラー回数カウンタを表示
}
/*
https://blog.zeruns.com
*/
Delay_ms(100); // 100ミリ秒遅延
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; // 桁数の値を返す
}
SHTC3.c
#include "stm32f10x.h"
#include "Delay.h"
/*SHTC3アドレス*/
#define SHTC3_ADDRESS 0xE0
/*どのI2Cを使用するか設定*/
#define I2Cx I2C1
/*
https://blog.zeruns.com
*/
/**
* @brief CRCチェック、CRC多項式:x^8+x^5+x^4+1、つまり0x31
* @param DAT チェック対象データ
* @retval チェックコード
*/
uint8_t SHTC3_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 SHTC3_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 SHTC3_I2C_STOP(){
I2C_GenerateSTOP(I2Cx, ENABLE);//ストップ信号送信
}
/**
* @brief 2バイトデータ送信
* @param MSB 上位8ビット
* @param LSB 下位8ビット
* @retval なし
*/
void SHTC3_WriteByte(uint8_t MSB,uint8_t LSB)
{
SHTC3_I2C_START(); // スタート信号送信
I2C_Send7bitAddress(I2Cx, SHTC3_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 SHTC3_ReadData()
{
while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//EV7イベント検出
return I2C_ReceiveData(I2Cx);//データ読み取り&返却
}
/*SHTC3ソフトウェアリセット*/
void SHTC3_SoftReset(void)
{
SHTC3_WriteByte(0x80,0x5D); // SHTC3リセット
}
/*ピン初期化*/
void SHTC3_I2C_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アドレス、不要なら適当でOK
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有効
SHTC3_WriteByte(0X35,0X17);// SHTC3ウェイクアップ
Delay_us(200);
}
/**
* @brief SHTC3データ読み取り
* @param *Hum 湿度
* @param *Temp 温度
* @retval 1 - 読み取り成功;0 - 読み取り失敗
*/
uint8_t ReadSHTC3(float *Hum,float *Temp)
{
uint16_t HumData,TempData,HumCRC,TempCRC;// 読み取りデータ格納変数宣言
/*SHTC3_WriteByte(0X35,0X17);// SHTC3ウェイクアップ
Delay_us(300);*/
SHTC3_WriteByte(0X5C,0X24);// コマンド送信、湿度データ先読み、クロックストレッチ(測定中SCLをローに保持、バス占有)
SHTC3_I2C_START();// スタート信号送信
I2C_Send7bitAddress(I2Cx,SHTC3_ADDRESS,I2C_Direction_Receiver);// デバイス読み取りアドレス送信
while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);// EV6イベント検出
HumData = SHTC3_ReadData(); // 湿度上位8ビット読み取り
HumData=HumData<<8; // 8ビット左シフト
HumData |= SHTC3_ReadData();// 湿度下位8ビット読み取り
HumCRC = SHTC3_ReadData(); // 湿度CRC読み取り
TempData = SHTC3_ReadData();// 温度上位8ビット読み取り
TempData=TempData<<8; // 8ビット左シフト
TempData |= SHTC3_ReadData();// 温度下位8ビット読み取り
TempCRC = SHTC3_ReadData(); // 温度CRC読み取り
SHTC3_I2C_STOP(); // ストップ信号送信
//SHTC3_WriteByte(0XB0,0X98);// スリープコマンド送信
if( SHTC3_CRC_CAL(HumData)==HumCRC && SHTC3_CRC_CAL(TempData)==TempCRC ){ // 受信データCRCチェック
*Hum = (float)HumData*100/65536; // 受信16ビットデータを湿度10進数に変換
*Temp = (float)TempData*175/65536-45; // 受信16ビットデータを温度10進数に変換
return 1;
}
else{
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アドレス、不要なら適当でOK
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_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ピンHW設定
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画面クリア
}
部分内容参考以下两篇文章:
https://blog.csdn.net/mj475002864/article/details/114027993
https://blog.csdn.net/k666499436/article/details/124686559
おすすめ記事
- 高コスパで安価な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






