prettyprint

2025年3月18日 星期二

[Raspberry Pi Pico C-SDK] Simple electronic compass using HMC5883L 3-axis digital compass

 介紹使用HMC5883L三軸數位羅盤,詳細內容如下列影片所示。

有關4-line serial TFT libarary請參閱:

[Raspberry Pi Pico (c-sdk)] Display: Ep 5 :TFT LCD 4-lines Serial(SPI) Driver -- A single Frame(128x160) takes only  11~12ms

成果影片


程式碼:


  • CMakeLists.txt

# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
    set(USERHOME $ENV{USERPROFILE})
else()
    set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.1.1)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.1.1)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
    include(${picoVscode})
endif()
# ====================================================================================
# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")

set(PICO_BOARD pico CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
  message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()

project(compass C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(compass compass.c )

pico_set_program_name(compass "compass")
pico_set_program_version(compass "0.1")

pico_enable_stdio_uart(compass 0)
pico_enable_stdio_usb(compass 1)

# Add the standard library to the build
target_link_libraries(compass
        pico_stdlib)

# Add the standard include files to the build
target_include_directories(compass PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}
  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)

# Add any user requested libraries
target_link_libraries(compass 
        hardware_i2c
        hardware_dma
        )
add_subdirectory(pico_tft)
target_link_libraries( compass 
      pico_tft
      )

pico_add_extra_outputs(compass)


  • compass.c

#include <stdio.h>
#include "stdlib.h"
#include "pico/stdlib.h"
#include "hardware/i2c.h"

#include "hardware/dma.h"
#include "hardware/pio.h"

#include "pico_tft.h"
#include "tft_string.h"
#include "fonts/font_fixedsys_mono_16.h"
#include "compass_img.h"
#include "string.h"
#include "registers.h"

#include "math.h"

#define I2C_PORT i2c0
#define I2C_SDA 16
#define I2C_SCL 17

#define DEVICE_ADDR   0x1E 

typedef struct _rotate_obj_t {
    uint8_t *srcimg;
    uint8_t *destimg;
    uint16_t width;
    uint16_t height;
    uint16_t angle;
} rotate_obj_t;


int16_t x_offset=-64;
int16_t z_offset= 76;

int main()
{
    uint8_t cmd_buf[4];
    uint8_t read_data[7];
    stdio_init_all();

    rotate_obj_t *rotate_img = (rotate_obj_t*) calloc(1, sizeof(rotate_obj_t));
    uint8_t *read_buff = (uint8_t*) calloc(TFT_WIDTH*TFT_HEIGHT*2, sizeof(uint8_t));  //frame buffer
    if (!read_buff) {
        printf("alloc memory error\n");
        return 0;
    }
     uint8_t *write_buff = (uint8_t*) calloc(TFT_WIDTH*TFT_HEIGHT*2, sizeof(uint8_t));  //frame buffer
    if (!write_buff) {
        printf("alloc memory error\n");
        return 0;
    }
    tft_init();
    tft_fill_rect(0,0, TFT_WIDTH, TFT_HEIGHT, 0xffff);

    // I2C Initialisation. Using it at 400Khz.
    i2c_init(I2C_PORT, 400*1000);
    
    gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
    gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);
    gpio_pull_up(I2C_SDA);
    gpio_pull_up(I2C_SCL);


    // init 8-average, 15 Hz default, normal measurement
    cmd_buf[0] = 0x00;
    cmd_buf[1] = 0x70;
    i2c_write_blocking(i2c0, DEVICE_ADDR,cmd_buf, 2,true);
    cmd_buf[0] = 0x01;
    cmd_buf[1] = 0xa0; //0xa0
    i2c_write_blocking(i2c0, DEVICE_ADDR,cmd_buf, 2,true);
    
    int16_t x,y,z;
    double heading;

    memcpy(read_buff, image_data_compass, TFT_WIDTH*TFT_WIDTH*2);
    tft_rotate_image((uint16_t*)read_buff, (uint16_t*)write_buff, TFT_WIDTH, TFT_WIDTH, 90);
    tft_set_address_window (0, 0, TFT_WIDTH, TFT_WIDTH);
    tft_cmd(TFT_MEMORYWRITE, TFT_WIDTH*TFT_WIDTH*2,  (uint8_t*)write_buff);

    char buf_heading[10];
    while(1) {
        //Single-Measurement Mode (Default).
        cmd_buf[0] = 0x02;
        cmd_buf[1] = 0x01; //0x00
        i2c_write_blocking(i2c0, DEVICE_ADDR,cmd_buf, 2,false);
        sleep_ms(10);

        cmd_buf[0] = 0x03;
        i2c_write_blocking(i2c0, DEVICE_ADDR,cmd_buf, 1,false);    
        i2c_read_blocking(i2c0, DEVICE_ADDR, read_data, 6, false);

       
        x = ((uint16_t)read_data[0]) << 8 | read_data[1];
        z = ((uint16_t)read_data[2]) << 8 | read_data[3];
        y = ((uint16_t)read_data[4]) << 8 | read_data[5];

        
        printf("%d, %d, %d \n", x,y,z );  // print out for calibration           

        heading = atan2(x-x_offset,z-z_offset)*57.3; //180/PI

        if(heading < 0) heading+=360;
        
        //heading=360-heading; // N=0/360, E=90, S=180, W=270
        tft_rotate_image((uint16_t*)read_buff, (uint16_t*)write_buff, TFT_WIDTH, TFT_WIDTH, 360-heading);
        tft_set_address_window (0, 0, TFT_WIDTH, TFT_WIDTH);
        tft_cmd(TFT_MEMORYWRITE, TFT_WIDTH*TFT_WIDTH*2,  (uint8_t*)write_buff);
        sprintf(buf_heading, "%03.02f   ", heading);
        tft_draw_string_withbg(10, TFT_WIDTH+4, buf_heading, 0xf800, 0xffff, &font_fixedsys_mono_16);

        
    }

    return 0;
}

  • compass_img.h:
    使用lcd-image-converter軟體產生。

沒有留言:

張貼留言