Advertisement
Guest User

Raspberry Pi picoで気温、湿度、気圧センサーのBME280をI2Cで使う

a guest
Feb 5th, 2021
323
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.94 KB | None | 0 0
  1. Raspberry Pi picoで気温、湿度、気圧センサーのBME280をI2Cで使う
  2.  
  3. exsampleのbme280_spi.cを参考にI2C版を作ってみました
  4. 以下にRaspberry Pi 4上でビルドする方法を説明します。
  5.  
  6. (~/pico/pico-sdkにpico-sdkが入ってると仮定してます。
  7. 違う場所にpico-sdkを入れた人は適宜読み替えてください)
  8.  
  9.  
  10. 1.プロジェクトを置きたい場所にプロジェクトのディレクトリを作る
  11.  
  12. mkdir test_bme280_i2c_usb01
  13.  
  14. cd test_bme280_i2c_usb01
  15.  
  16.  
  17. 2.ソースの作成
  18.  
  19. 一番下にtest_bme280_i2c_usb01.cのプログラムソースを記載してます
  20.  
  21.  
  22. 3.CMakeLists.txtの作成
  23.  
  24.  
  25. vi CMakeLists.txt
  26.  
  27. 下記の内容で作成
  28.  
  29. cmake_minimum_required(VERSION 3.12)
  30.  
  31. # Pull in PICO SDK (must be before project)
  32. include(~/pico/pico-sdk/external/pico_sdk_import.cmake)
  33.  
  34. project(test_bme280_i2c_usb01_project)
  35. set(CMAKE_C_STANDARD 11)
  36. set(CMAKE_CXX_STANDARD 17)
  37.  
  38. # Initialize the SDK
  39. pico_sdk_init()
  40. add_executable(test_bme280_i2c_usb01 test_bme280_i2c_usb01.c)
  41. target_link_libraries(test_bme280_i2c_usb01 pico_stdlib hardware_i2c)
  42.  
  43. pico_enable_stdio_usb(test_bme280_i2c_usb01 1)
  44. pico_enable_stdio_uart(test_bme280_i2c_usb01 0)
  45.  
  46. pico_add_extra_outputs(test_bme280_i2c_usb01)
  47.  
  48.  
  49. 上記のようにI2Cを使う場合はtarget_link_librariesでhardware_i2cを指定する必要があります
  50.  
  51.  
  52. 4.buildディレクトリの作成し、カレントディレクトリをbuildに移動
  53.  
  54. mkdir build
  55.  
  56. cd build
  57.  
  58.  
  59.  
  60. 5.ビルドの実行
  61.  
  62. export PICO_SDK_PATH=~/pico/pico-sdk
  63.  
  64. cmake ..
  65.  
  66. make
  67.  
  68.  
  69. cmake ..の..の部分は親のディレクトリを指してます
  70. つまり、今いるbuildディレクトリの上にCMakeLists.txtがあることを指定してます
  71. ビルドが終わるとbuildディレクトリ内に
  72. test_bme280_i2c_usb01.bin test_bme280_i2c_usb01.dis test_bme280_i2c_usb01.elf test_bme280_i2c_usb01.elf.map test_bme280_i2c_usb01.hex test_bme280_i2c_usb01.uf2
  73. ができます
  74.  
  75. picoへの書き込みに使うのはtest_bme280_i2c_usb01.uf2です
  76.  
  77.  
  78. 6.picoへの書き込み
  79.  
  80. picoのbootselボタンを押しながらpicoをRaspberry Pi 4のUSB2ポートに差します
  81. すると他にUSBストレージを何も差してなければ新たに/dev/sda1ができます
  82. 他にUSBストレージが差してある場合は/dev/sdb1、/dev/sdc1、/dev/sdd1など
  83. すでにあるストレージデバイスの次の番号のストレージデバイスが/devに追加されます
  84. Raspberry Pi 4が自動マウントした場合
  85. そこにビルドで出来上がったtest_bme280_i2c_usb01.uf2をコピーして
  86. syncを1回実行してからsudo umountでマウントを解除します
  87. 自動マウントしない場合は
  88. 新しくできたデバイスが/dev/sda1の場合は下記のようになります
  89.  
  90. sudo mount -t vfat /dev/sda1 /mnt
  91. sudo cp test_bme280_i2c_usb01.uf2 /mnt
  92. sync
  93. sudo umount /mnt
  94.  
  95.  
  96. 7.USBシリアルに接続する
  97.  
  98. test_bme280_i2c_usb01はUSBシリアルを使うようにプログラムされているので
  99. test_bme280_i2c_usb01.uf2をpicoへコピーしてマウントを解除したあとにpicoが再起動して
  100. /dev/ttyACM0ができます
  101.  
  102. minicomで/dev/ttyACM0に接続します
  103.  
  104. minicomがない場合は
  105. sudo apt install minicom
  106. でインストールしてください
  107.  
  108. minicom -D /dev/ttyACM0 -b 115200
  109.  
  110.  
  111.  
  112.  
  113. minicomの終了方法はCTRL+aを押した後にzを押すとメニューが出ます
  114. メニューが出たらxを押して「はい」を選択すると終了します
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121. 以下、test_bme280_i2c_usb01.cのソースになります
  122. このソースではBME280のI2Cアドレスは0x76にしてあります。
  123. BME280のI2Cアドレスが違う場合は変更してください
  124. I2CはpicoのI2C0を使ってます(SDAがGPIO4(6番ピン)、SCLがGPIO5(7番ピン))
  125.  
  126.  
  127.  
  128.  
  129.  
  130. #include <stdio.h>
  131. #include "pico/stdlib.h"
  132. #include "hardware/i2c.h"
  133. #include "pico/binary_info.h"
  134.  
  135. #define BME280_DEVICE_ADDRESS 0x76
  136. #define I2C_PORT i2c0
  137.  
  138. #define MODE_SLEEP 0b00
  139. #define MODE_FORCED 0b01
  140. #define MODE_NORMAL 0b11
  141.  
  142. void i2c_write_byte_non_regster(int addr, uint8_t val);
  143. void i2c_write_byte(int addr, uint8_t reg, uint8_t val);
  144. void i2c_write_half_word_little(int addr, uint8_t reg, uint16_t val);
  145. void i2c_write_half_word_big(int addr, uint8_t reg, uint16_t val);
  146. uint8_t i2c_read_byte(int addr, uint8_t reg);
  147. uint16_t i2c_read_half_word_little(int addr, uint8_t reg);
  148. uint16_t i2c_read_half_word_big(int addr, uint8_t reg);
  149. void i2c_read_buf(int addr, uint8_t reg, uint8_t *buf, int16_t len);
  150. int32_t bme280_read_raw_pressure();
  151. int32_t bme280_read_raw_temperature();
  152. int32_t bme280_read_raw_humidity();
  153. bool bme280_isMeasuring(void);
  154. void bme280_setMode(uint8_t mode);
  155. void bme280_setStandbyTime(uint8_t timeSetting);
  156. void bme280_setFilter(uint8_t filterSetting);
  157.  
  158. int32_t t_fine;
  159.  
  160. uint16_t dig_T1;
  161. int16_t dig_T2, dig_T3;
  162. uint16_t dig_P1;
  163. int16_t dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
  164. uint8_t dig_H1, dig_H3;
  165. int8_t dig_H6;
  166. int16_t dig_H2, dig_H4, dig_H5;
  167.  
  168. int32_t compensate_temp(int32_t adc_T) {
  169. int32_t var1, var2, T;
  170. var1 = ((((adc_T >> 3) - ((int32_t) dig_T1 << 1))) * ((int32_t) dig_T2)) >> 11;
  171. var2 = (((((adc_T >> 4) - ((int32_t) dig_T1)) * ((adc_T >> 4) - ((int32_t) dig_T1))) >> 12) * ((int32_t) dig_T3))
  172. >> 14;
  173.  
  174. t_fine = var1 + var2;
  175. T = (t_fine * 5 + 128) >> 8;
  176. return T;
  177. }
  178.  
  179. uint32_t compensate_pressure(int32_t adc_P) {
  180. int32_t var1, var2;
  181. uint32_t p;
  182. var1 = (((int32_t) t_fine) >> 1) - (int32_t) 64000;
  183. var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((int32_t) dig_P6);
  184. var2 = var2 + ((var1 * ((int32_t) dig_P5)) << 1);
  185. var2 = (var2 >> 2) + (((int32_t) dig_P4) << 16);
  186. var1 = (((dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((int32_t) dig_P2) * var1) >> 1)) >> 18;
  187. var1 = ((((32768 + var1)) * ((int32_t) dig_P1)) >> 15);
  188. if (var1 == 0)
  189. return 0;
  190.  
  191. p = (((uint32_t) (((int32_t) 1048576) - adc_P) - (var2 >> 12))) * 3125;
  192. if (p < 0x80000000)
  193. p = (p << 1) / ((uint32_t) var1);
  194. else
  195. p = (p / (uint32_t) var1) * 2;
  196.  
  197. var1 = (((int32_t) dig_P9) * ((int32_t) (((p >> 3) * (p >> 3)) >> 13))) >> 12;
  198. var2 = (((int32_t) (p >> 2)) * ((int32_t) dig_P8)) >> 13;
  199. p = (uint32_t) ((int32_t) p + ((var1 + var2 + dig_P7) >> 4));
  200.  
  201. return p;
  202. }
  203.  
  204. uint32_t compensate_humidity(int32_t adc_H) {
  205. int32_t v_x1_u32r;
  206. v_x1_u32r = (t_fine - ((int32_t) 76800));
  207. v_x1_u32r = (((((adc_H << 14) - (((int32_t) dig_H4) << 20) - (((int32_t) dig_H5) * v_x1_u32r)) +
  208. ((int32_t) 16384)) >> 15) * (((((((v_x1_u32r * ((int32_t) dig_H6)) >> 10) * (((v_x1_u32r *
  209. ((int32_t) dig_H3)) >> 11) + ((int32_t) 32768))) >> 10) + ((int32_t) 2097152)) *
  210. ((int32_t) dig_H2) + 8192) >> 14));
  211. v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t) dig_H1)) >> 4));
  212. v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
  213. v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
  214.  
  215. return (uint32_t) (v_x1_u32r >> 12);
  216. }
  217.  
  218.  
  219. /* This function reads the manufacturing assigned compensation parameters from the device */
  220. void read_compensation_parameters() {
  221. uint8_t buffer[26];
  222.  
  223. i2c_read_buf(BME280_DEVICE_ADDRESS, (uint8_t)0x88, buffer, 24);
  224.  
  225. dig_T1 = buffer[0] | (buffer[1] << 8);
  226. dig_T2 = buffer[2] | (buffer[3] << 8);
  227. dig_T3 = buffer[4] | (buffer[5] << 8);
  228.  
  229. dig_P1 = buffer[6] | (buffer[7] << 8);
  230. dig_P2 = buffer[8] | (buffer[9] << 8);
  231. dig_P3 = buffer[10] | (buffer[11] << 8);
  232. dig_P4 = buffer[12] | (buffer[13] << 8);
  233. dig_P5 = buffer[14] | (buffer[15] << 8);
  234. dig_P6 = buffer[16] | (buffer[17] << 8);
  235. dig_P7 = buffer[18] | (buffer[19] << 8);
  236. dig_P8 = buffer[20] | (buffer[21] << 8);
  237. dig_P9 = buffer[22] | (buffer[23] << 8);
  238.  
  239. dig_H1 = i2c_read_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xA1);
  240.  
  241. i2c_read_buf(BME280_DEVICE_ADDRESS, (uint8_t)0xE1, buffer, 7);
  242.  
  243. dig_H2 = buffer[0] | ((int16_t)buffer[1] << 8);
  244. dig_H3 = (int8_t) buffer[2];
  245. dig_H4 = (int16_t)buffer[3] << 4 | (buffer[4] & 0xf);
  246. dig_H5 = ((buffer[4] >> 4) & 0x0f) | ((int16_t)buffer[5] << 4);
  247. dig_H6 = (int8_t) buffer[6];
  248. }
  249.  
  250. static void bme280_read_raw(int32_t *humidity, int32_t *pressure, int32_t *temperature) {
  251. uint8_t buffer[8];
  252.  
  253. i2c_read_buf(BME280_DEVICE_ADDRESS, (uint8_t)0xF7, buffer, 8);
  254. *pressure = ((uint32_t) buffer[0] << 12) | ((uint32_t) buffer[1] << 4) | ((uint32_t)buffer[2] >> 4);
  255. *temperature = ((uint32_t) buffer[3] << 12) | ((uint32_t) buffer[4] << 4) | ((uint32_t)buffer[5] >> 4);
  256. *humidity = ((uint32_t) buffer[6] << 8) | ((uint32_t)buffer[7]);
  257. }
  258.  
  259.  
  260. int32_t bme280_read_raw_pressure() {
  261. uint8_t buffer[3];
  262. int32_t pressure;
  263.  
  264. i2c_read_buf(BME280_DEVICE_ADDRESS, (uint8_t)0xF7, buffer, 3);
  265. pressure = ((uint32_t) buffer[0] << 12) | ((uint32_t) buffer[1] << 4) | ((uint32_t)buffer[2] >> 4);
  266. return pressure;
  267. }
  268.  
  269. int32_t bme280_read_raw_temperature() {
  270. uint8_t buffer[3];
  271. int32_t temperature;
  272.  
  273. i2c_read_buf(BME280_DEVICE_ADDRESS, (uint8_t)0xFA, buffer, 3);
  274. temperature = ((uint32_t) buffer[0] << 12) | ((uint32_t) buffer[1] << 4) | ((uint32_t)buffer[2] >> 4);
  275. return temperature;
  276. }
  277.  
  278. int32_t bme280_read_raw_humidity() {
  279. uint8_t buffer[3];
  280. int32_t humidity;
  281.  
  282. i2c_read_buf(BME280_DEVICE_ADDRESS, (uint8_t)0xFD, buffer, 2);
  283. humidity = ((uint32_t) buffer[0] << 8) | ((uint32_t)buffer[1]);
  284. return humidity;
  285. }
  286.  
  287.  
  288. int main() {
  289. stdio_init_all();
  290.  
  291. printf("Hello, bme280! Reading raw data from registers via I2C...\n");
  292.  
  293. // This example will use SPI0 at 0.5MHz.
  294. i2c_init(I2C_PORT, 400 * 1000); // I2C clock 400kHz
  295. gpio_set_function(4, GPIO_FUNC_I2C);
  296. gpio_set_function(5, GPIO_FUNC_I2C);
  297. gpio_pull_up(4);
  298. gpio_pull_up(5);
  299.  
  300. // Make the I2C pins available to picotool
  301. bi_decl( bi_2pins_with_func(4, 5, GPIO_FUNC_I2C));
  302.  
  303. // BME280 Reset
  304. i2c_write_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xE0, (uint8_t)0xB6);
  305. sleep_ms(100);
  306.  
  307. // See if SPI is working - interrograte the device for its I2C ID number, should be 0x60
  308. uint8_t id;
  309. id = i2c_read_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xD0);
  310. printf("Chip ID is 0x%x\n", id);
  311.  
  312. read_compensation_parameters();
  313.  
  314. i2c_write_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF2, (uint8_t)0x1); // Humidity oversampling register - going for x1
  315. i2c_write_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF4, (uint8_t)0x27);// Set rest of oversampling modes and run mode to normal
  316. bme280_setStandbyTime(0);
  317. bme280_setFilter(0);
  318. bme280_setMode(MODE_SLEEP);
  319.  
  320. int32_t humidity, pressure, temperature;
  321.  
  322. while (1) {
  323. bme280_setMode(MODE_FORCED);
  324. while (!bme280_isMeasuring()) sleep_ms(1); // Wait before the start of measurement
  325. while ( bme280_isMeasuring()) sleep_ms(1); // Wait during a measurement
  326.  
  327. bme280_read_raw(&humidity, &pressure, &temperature);
  328.  
  329. // These are the raw numbers from the chip, so we need to run through the
  330. // compensations to get human understandable numbers
  331. pressure = compensate_pressure(pressure);
  332. temperature = compensate_temp(temperature);
  333. humidity = compensate_humidity(humidity);
  334.  
  335. printf("Humidity : %.2f%%\r\n", humidity / 1024.0);
  336. printf("Pressure : %.2fhPa\r\n", pressure / 100.0);
  337. printf("Temp : %.2fC\r\n", temperature / 100.0);
  338. printf("\n");
  339.  
  340. sleep_ms(5000);
  341. }
  342.  
  343. return 0;
  344. }
  345.  
  346. void i2c_write_byte_non_register(int addr, uint8_t val) {
  347. i2c_write_blocking(I2C_PORT, addr, &val, 1, false);
  348. }
  349.  
  350. void i2c_write_byte(int addr, uint8_t reg, uint8_t val) {
  351. uint8_t buf[2];
  352. buf[0] = reg;
  353. buf[1] = val;
  354. i2c_write_blocking(I2C_PORT, addr, buf, 2, false);
  355. }
  356.  
  357. void i2c_write_half_word_little(int addr, uint8_t reg, uint16_t val) {
  358. uint8_t buf[3];
  359. buf[0] = reg;
  360. buf[1] = (uint8_t)(val & 0x00ff);
  361. buf[2] = (uint8_t)((val >> 8) & 0x00ff);
  362. i2c_write_blocking(I2C_PORT, addr, buf, 3, false);
  363. }
  364.  
  365. void i2c_write_half_word_big(int addr, uint8_t reg, uint16_t val) {
  366. uint8_t buf[3];
  367. buf[0] = reg;
  368. buf[1] = (uint8_t)((val >> 8) & 0x00ff);
  369. buf[2] = (uint8_t)(val & 0x00ff);
  370. i2c_write_blocking(I2C_PORT, addr, buf, 3, false);
  371. }
  372.  
  373. uint8_t i2c_read_byte(int addr, uint8_t reg) {
  374. uint8_t buf;
  375. i2c_write_blocking(I2C_PORT, addr, &reg, 1, true);
  376. i2c_read_blocking(I2C_PORT, addr, &buf, 1, false); // False - finished with bus
  377. return buf;
  378. }
  379.  
  380. uint16_t i2c_read_half_word_little(int addr, uint8_t reg) {
  381. uint8_t buf[2];
  382. uint16_t ret;
  383. i2c_write_blocking(I2C_PORT, addr, &reg, 1, true);
  384. i2c_read_blocking(I2C_PORT, addr, buf, 2, false); // False - finished with bus
  385.  
  386. ret = (uint16_t)(((uint16_t)buf[1] << 8) | (uint16_t)buf[0]);
  387. return ret;
  388. }
  389.  
  390. uint16_t i2c_read_half_word_big(int addr, uint8_t reg) {
  391. uint8_t buf[2];
  392. uint16_t ret;
  393. i2c_write_blocking(I2C_PORT, addr, &reg, 1, true);
  394. i2c_read_blocking(I2C_PORT, addr, buf, 2, false); // False - finished with bus
  395.  
  396. ret = (uint16_t)(((uint16_t)buf[0] << 8) | (uint16_t)buf[1]);
  397. return ret;
  398. }
  399.  
  400. void i2c_read_buf(int addr, uint8_t reg, uint8_t *buf, int16_t len) {
  401.  
  402. i2c_write_blocking(I2C_PORT, addr, &reg, 1, true);
  403. i2c_read_blocking(I2C_PORT, addr, buf, len, false); // False - finished with bus
  404. }
  405.  
  406. bool bme280_isMeasuring(void) {
  407. uint8_t stat = i2c_read_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF3);
  408. return(stat & (1<<3));
  409. }
  410.  
  411. void bme280_setMode(uint8_t mode) {
  412. if(mode > 0b11) mode = 0;
  413.  
  414. uint8_t controlData = i2c_read_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF4);
  415. controlData &= ~( (1<<1) | (1<<0) ); //Clear the mode[1:0] bits
  416. controlData |= mode;
  417. i2c_write_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF4, controlData);
  418. }
  419.  
  420. // 0, 0.5ms
  421. // 1, 62.5ms
  422. // 2, 125ms
  423. // 3, 250ms
  424. // 4, 500ms
  425. // 5, 1000ms
  426. // 6, 10ms
  427. // 7, 20ms
  428. void bme280_setStandbyTime(uint8_t timeSetting) {
  429. if(timeSetting > 0b111) timeSetting = 0;
  430.  
  431. uint8_t controlData = i2c_read_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF5);
  432. controlData &= ~( (1<<7) | (1<<6) | (1<<5) ); //Clear the 7/6/5 bits
  433. controlData |= (timeSetting << 5); //Align with bits 7/6/5
  434. i2c_write_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF5, controlData);
  435. }
  436.  
  437. // 0, filter off
  438. // 1, coefficients = 2
  439. // 2, coefficients = 4
  440. // 3, coefficients = 8
  441. // 4, coefficients = 16
  442. void bme280_setFilter(uint8_t filterSetting) {
  443. if(filterSetting > 0b111) filterSetting = 0;
  444.  
  445. uint8_t controlData = i2c_read_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF5);
  446. controlData &= ~( (1<<4) | (1<<3) | (1<<2) ); //Clear the 4/3/2 bits
  447. controlData |= (filterSetting << 2); //Align with bits 4/3/2
  448. i2c_write_byte(BME280_DEVICE_ADDRESS, (uint8_t)0xF5, controlData);
  449. }
  450.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement