Converted Code from Codecell to RPI Pico

This commit is contained in:
Ulysse Cura 2025-05-08 20:48:35 +02:00
parent e0bb4b3ae8
commit 48e22faf62
11 changed files with 1245 additions and 6 deletions

1
Code/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

View File

@ -12,13 +12,9 @@ pico_sdk_init()
add_executable(motion_controller add_executable(motion_controller
src/main.c src/main.c
src/robot.c
src/motors.c
src/gyro.c src/gyro.c
src/motion_control.c src/ssd1306.c
src/i2c_master.c src/io.c
src/udp_client.c
src/udp_buffer.c
) )
target_link_libraries(motion_controller target_link_libraries(motion_controller

137
Code/src/gyro.c Normal file
View File

@ -0,0 +1,137 @@
#include "include/gyro.h"
#include <stdint.h>
#include <hardware/i2c.h>
#include "include/io.h"
//#include <stdio.h>
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Thank you Keuronde ! *
* https://git.poivron-robotique.fr/Keuronde/Holonome_2024/src/branch/Demo_2025_03/gyro_L3GD20H.c *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#define SAMPLE_MIN_ELAPSED_TIME 2 // ms
#define DPS_PER_DIGIT 0.00875f
int init_gyro(void)
{
// Verify gyro initialisation
uint8_t reg = 0x0f;
uint8_t data;
i2c_write_blocking(I2C_MASTER_INSTANCE, I2C_GYRO_ADDRESS, &reg, 1, false);
i2c_read_blocking(I2C_MASTER_INSTANCE, I2C_GYRO_ADDRESS, &data, 1, false);
if(data != 0xd7) return -1;
// Configure gyro
const uint8_t CTRL1_REG = 0x20;
const uint8_t CTRL1_CONFIG = 0b11101111; // DR : 11 // BW : 10 // PD : 1 // Zen : 1 // Xen : 1 // Yen : 1 //
uint8_t config[] = {CTRL1_REG, CTRL1_CONFIG};
uint8_t config_verification;
i2c_write_blocking(I2C_MASTER_INSTANCE, I2C_GYRO_ADDRESS, config, 2, false);
i2c_write_blocking(I2C_MASTER_INSTANCE, I2C_GYRO_ADDRESS, &CTRL1_CONFIG, 1, false);
i2c_read_blocking(I2C_MASTER_INSTANCE, I2C_GYRO_ADDRESS, &config_verification, 1, false);
if(config_verification != config[1]) return -1;
io.gyro_data.x_angle = 0.0f;
io.gyro_data.y_angle = 0.0f;
io.gyro_data.z_angle = 0.0f;
return 0;
}
static inline void __attribute__((always_inline)) gyro_read(int16_t *x, int16_t *y, int16_t *z)
{
const uint8_t X_OUT_L_REG = 0x28;
uint8_t reg = X_OUT_L_REG | 0x80; // 0x80 for auto incrementing
uint8_t data[6];
i2c_write_blocking(I2C_MASTER_INSTANCE, I2C_GYRO_ADDRESS, &reg, 1, false);
i2c_read_blocking(I2C_MASTER_INSTANCE, I2C_GYRO_ADDRESS, data, 1, false);
*x = (int16_t)((data[1] << 8) | data[0]);
*y = (int16_t)((data[3] << 8) | data[2]);
*z = (int16_t)((data[5] << 8) | data[4]);
}
void gyro_calibrate(void)
{
const uint nb_samples = 10000;
int16_t x, y, z;
int32_t x_sum = 0, y_sum = 0, z_sum = 0;
for(uint i = 0; i < nb_samples; i++)
{
gyro_read(&x, &y, &z);
x_sum += x;
y_sum += y;
z_sum += z;
sleep_ms(SAMPLE_MIN_ELAPSED_TIME);
//printf(">cal_x:%d\n", x);
//printf(">cal_y:%d\n", y);
//printf(">cal_z:%d\n", z);
}
io.gyro_data.x_offset = (float)x_sum / (float)nb_samples * DPS_PER_DIGIT;
io.gyro_data.y_offset = (float)y_sum / (float)nb_samples * DPS_PER_DIGIT;
io.gyro_data.z_offset = (float)z_sum / (float)nb_samples * DPS_PER_DIGIT;
//printf("\nx_cal:%.5f\n", io.gyro_data.x_offset);
//printf("\ny_cal:%.5f\n", io.gyro_data.y_offset);
//printf("\nz_cal:%.5f\n", io.gyro_data.z_offset);
}
static inline void __attribute__((always_inline)) gyro_get_dps(double* x_dps, double* y_dps, double* z_dps)
{
int16_t x, y, z;
gyro_read(&x, &y, &z);
*x_dps = x * DPS_PER_DIGIT;
*y_dps = y * DPS_PER_DIGIT;
*z_dps = z * DPS_PER_DIGIT;
}
void gyro_update(void)
{
static double elapsed_since_sample_ms = 10.0;
elapsed_since_sample_ms += io.delta_time_ms;
if(elapsed_since_sample_ms >= SAMPLE_MIN_ELAPSED_TIME)
{
double x_dps, y_dps, z_dps;
gyro_get_dps(&x_dps, &y_dps, &z_dps);
x_dps -= io.gyro_data.x_offset;
y_dps -= io.gyro_data.y_offset;
z_dps -= io.gyro_data.z_offset;
io.gyro_data.x_angle += x_dps * elapsed_since_sample_ms / 1000.0f;
io.gyro_data.y_angle += y_dps * elapsed_since_sample_ms / 1000.0f;
io.gyro_data.z_angle += z_dps * elapsed_since_sample_ms / 1000.0f;
while(io.gyro_data.x_angle > 180) io.gyro_data.x_angle -= 360;
while(io.gyro_data.x_angle < -180) io.gyro_data.x_angle += 360;
while(io.gyro_data.y_angle > 180) io.gyro_data.y_angle -= 360;
while(io.gyro_data.y_angle < -180) io.gyro_data.y_angle += 360;
while(io.gyro_data.z_angle > 180) io.gyro_data.z_angle -= 360;
while(io.gyro_data.z_angle < -180) io.gyro_data.z_angle += 360;
//printf(">gyro_x_angle:%f\n", io.gyro_data.x_angle);
//printf(">gyro_y_angle:%f\n", io.gyro_data.y_angle);
//printf(">gyro_z_angle:%f\n", io.gyro_data.z_angle);
elapsed_since_sample_ms = 0.0;
}
}

110
Code/src/include/font.h Normal file
View File

@ -0,0 +1,110 @@
#ifndef _inc_font
#define _inc_font
/*
* Format
* <height>, <width>, <additional spacing per char>,
* <first ascii char>, <last ascii char>,
* <data>
*/
const uint8_t font_8x5[] =
{
8, 5, 1, 32, 126,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5F, 0x00, 0x00,
0x00, 0x07, 0x00, 0x07, 0x00,
0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12,
0x23, 0x13, 0x08, 0x64, 0x62,
0x36, 0x49, 0x56, 0x20, 0x50,
0x00, 0x08, 0x07, 0x03, 0x00,
0x00, 0x1C, 0x22, 0x41, 0x00,
0x00, 0x41, 0x22, 0x1C, 0x00,
0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
0x08, 0x08, 0x3E, 0x08, 0x08,
0x00, 0x80, 0x70, 0x30, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x60, 0x60, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E,
0x00, 0x42, 0x7F, 0x40, 0x00,
0x72, 0x49, 0x49, 0x49, 0x46,
0x21, 0x41, 0x49, 0x4D, 0x33,
0x18, 0x14, 0x12, 0x7F, 0x10,
0x27, 0x45, 0x45, 0x45, 0x39,
0x3C, 0x4A, 0x49, 0x49, 0x31,
0x41, 0x21, 0x11, 0x09, 0x07,
0x36, 0x49, 0x49, 0x49, 0x36,
0x46, 0x49, 0x49, 0x29, 0x1E,
0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41,
0x14, 0x14, 0x14, 0x14, 0x14,
0x00, 0x41, 0x22, 0x14, 0x08,
0x02, 0x01, 0x59, 0x09, 0x06,
0x3E, 0x41, 0x5D, 0x59, 0x4E,
0x7C, 0x12, 0x11, 0x12, 0x7C,
0x7F, 0x49, 0x49, 0x49, 0x36,
0x3E, 0x41, 0x41, 0x41, 0x22,
0x7F, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x49, 0x49, 0x49, 0x41,
0x7F, 0x09, 0x09, 0x09, 0x01,
0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F,
0x00, 0x41, 0x7F, 0x41, 0x00,
0x20, 0x40, 0x41, 0x3F, 0x01,
0x7F, 0x08, 0x14, 0x22, 0x41,
0x7F, 0x40, 0x40, 0x40, 0x40,
0x7F, 0x02, 0x1C, 0x02, 0x7F,
0x7F, 0x04, 0x08, 0x10, 0x7F,
0x3E, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x09, 0x09, 0x09, 0x06,
0x3E, 0x41, 0x51, 0x21, 0x5E,
0x7F, 0x09, 0x19, 0x29, 0x46,
0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03,
0x3F, 0x40, 0x40, 0x40, 0x3F,
0x1F, 0x20, 0x40, 0x20, 0x1F,
0x3F, 0x40, 0x38, 0x40, 0x3F,
0x63, 0x14, 0x08, 0x14, 0x63,
0x03, 0x04, 0x78, 0x04, 0x03,
0x61, 0x59, 0x49, 0x4D, 0x43,
0x00, 0x7F, 0x41, 0x41, 0x41,
0x02, 0x04, 0x08, 0x10, 0x20,
0x00, 0x41, 0x41, 0x41, 0x7F,
0x04, 0x02, 0x01, 0x02, 0x04,
0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00,
0x20, 0x54, 0x54, 0x78, 0x40,
0x7F, 0x28, 0x44, 0x44, 0x38,
0x38, 0x44, 0x44, 0x44, 0x28,
0x38, 0x44, 0x44, 0x28, 0x7F,
0x38, 0x54, 0x54, 0x54, 0x18,
0x00, 0x08, 0x7E, 0x09, 0x02,
0x18, 0xA4, 0xA4, 0x9C, 0x78,
0x7F, 0x08, 0x04, 0x04, 0x78,
0x00, 0x44, 0x7D, 0x40, 0x00,
0x20, 0x40, 0x40, 0x3D, 0x00,
0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00,
0x7C, 0x04, 0x78, 0x04, 0x78,
0x7C, 0x08, 0x04, 0x04, 0x78,
0x38, 0x44, 0x44, 0x44, 0x38,
0xFC, 0x18, 0x24, 0x24, 0x18,
0x18, 0x24, 0x24, 0x18, 0xFC,
0x7C, 0x08, 0x04, 0x04, 0x08,
0x48, 0x54, 0x54, 0x54, 0x24,
0x04, 0x04, 0x3F, 0x44, 0x24,
0x3C, 0x40, 0x40, 0x20, 0x7C,
0x1C, 0x20, 0x40, 0x20, 0x1C,
0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44,
0x4C, 0x90, 0x90, 0x90, 0x7C,
0x44, 0x64, 0x54, 0x4C, 0x44,
0x00, 0x08, 0x36, 0x41, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00,
0x02, 0x01, 0x02, 0x04, 0x02,
};
#endif

18
Code/src/include/gyro.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef GYRO_H
#define GYRO_H
#define I2C_GYRO_ADDRESS 0x6b
typedef struct gyro_data_t {
float x_offset, y_offset, z_offset;
float x_angle, y_angle, z_angle;
} gyro_data_t;
// Check if gyro has correctly initialised and configure it for simple use
int init_gyro(void);
// Calibrate gyro
void gyro_calibrate(void);
// Update gyro data
void gyro_update(void);
#endif // GYRO_H

View File

53
Code/src/include/io.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef IO_H
#define IO_H
#include <pico/types.h>
#include "ssd1306.h"
#include "gyro.h"
#define I2C_MASTER_SDA_PIN 21
#define I2C_MASTER_SCL_PIN 20
#define I2C_MASTER_INSTANCE i2c0
#define PIN_TIRETTE 5
#define PIN_BUTTON_COLOR 6
#define PIN_MOTOR1 1
#define PIN_MOTOR2 2
#define PIN_SERVO 7
#define DANCING_ACTION_DELTA_ANGLE 2
#define GAIN_KD 50
#define ANGULAR_SPEED 120
typedef struct io_t {
double delta_time_ms;
bool motion_control_activated;
bool is_tirette_pulled;
bool is_color_blue;
bool is_dancing;
ssd1306_t *screen;
gyro_data_t gyro_data;
float target_dir;
float target_speed;
double time_ms;
} io_t;
extern io_t io;
void motor_control_activated(bool activated);
void init_io(void);
void update_io(void);
void set_dir_with_angular_speed(float dir);
void io_deinit(void);
#endif // IO_H

273
Code/src/include/ssd1306.h Normal file
View File

@ -0,0 +1,273 @@
/*
MIT License
Copyright (c) 2021 David Schramm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* @file ssd1306.h
*
* simple driver for ssd1306 displays
*/
#ifndef _inc_ssd1306
#define _inc_ssd1306
#include <hardware/i2c.h>
/**
* @brief defines commands used in ssd1306
*/
typedef enum {
SET_CONTRAST = 0x81,
SET_ENTIRE_ON = 0xA4,
SET_NORM_INV = 0xA6,
SET_DISP = 0xAE,
SET_MEM_ADDR = 0x20,
SET_COL_ADDR = 0x21,
SET_PAGE_ADDR = 0x22,
SET_DISP_START_LINE = 0x40,
SET_SEG_REMAP = 0xA0,
SET_MUX_RATIO = 0xA8,
SET_COM_OUT_DIR = 0xC0,
SET_DISP_OFFSET = 0xD3,
SET_COM_PIN_CFG = 0xDA,
SET_DISP_CLK_DIV = 0xD5,
SET_PRECHARGE = 0xD9,
SET_VCOM_DESEL = 0xDB,
SET_CHARGE_PUMP = 0x8D
} ssd1306_command_t;
/**
* @brief holds the configuration
*/
typedef struct {
uint8_t width; /**< width of display */
uint8_t height; /**< height of display */
uint8_t pages; /**< stores pages of display (calculated on initialization*/
uint8_t address; /**< i2c address of display*/
i2c_inst_t *i2c_i; /**< i2c connection instance */
bool external_vcc; /**< whether display uses external vcc */
uint8_t *buffer; /**< display buffer */
size_t bufsize; /**< buffer size */
} ssd1306_t;
/**
* @brief initialize display
*
* @param[in] p : pointer to instance of ssd1306_t
* @param[in] width : width of display
* @param[in] height : heigth of display
* @param[in] address : i2c address of display
* @param[in] i2c_instance : instance of i2c connection
*
* @return bool.
* @retval true for Success
* @retval false if initialization failed
*/
bool ssd1306_init(ssd1306_t *p, uint16_t width, uint16_t height, uint8_t address, i2c_inst_t *i2c_instance);
/**
* @brief deinitialize display
*
* @param[in] p : instance of display
*
*/
void ssd1306_deinit(ssd1306_t *p);
/**
* @brief turn off display
*
* @param[in] p : instance of display
*
*/
void ssd1306_poweroff(ssd1306_t *p);
/**
@brief turn on display
@param[in] p : instance of display
*/
void ssd1306_poweron(ssd1306_t *p);
/**
@brief set contrast of display
@param[in] p : instance of display
@param[in] val : contrast
*/
void ssd1306_contrast(ssd1306_t *p, uint8_t val);
/**
@brief set invert display
@param[in] p : instance of display
@param[in] inv : inv==0: disable inverting, inv!=0: invert
*/
void ssd1306_invert(ssd1306_t *p, uint8_t inv);
/**
@brief display buffer, should be called on change
@param[in] p : instance of display
*/
void ssd1306_show(ssd1306_t *p);
/**
@brief clear display buffer
@param[in] p : instance of display
*/
void ssd1306_clear(ssd1306_t *p);
/**
@brief clear pixel on buffer
@param[in] p : instance of display
@param[in] x : x position
@param[in] y : y position
*/
void ssd1306_clear_pixel(ssd1306_t *p, uint32_t x, uint32_t y);
/**
@brief draw pixel on buffer
@param[in] p : instance of display
@param[in] x : x position
@param[in] y : y position
*/
void ssd1306_draw_pixel(ssd1306_t *p, uint32_t x, uint32_t y);
/**
@brief draw line on buffer
@param[in] p : instance of display
@param[in] x1 : x position of starting point
@param[in] y1 : y position of starting point
@param[in] x2 : x position of end point
@param[in] y2 : y position of end point
*/
void ssd1306_draw_line(ssd1306_t *p, int32_t x1, int32_t y1, int32_t x2, int32_t y2);
/**
@brief clear square at given position with given size
@param[in] p : instance of display
@param[in] x : x position of starting point
@param[in] y : y position of starting point
@param[in] width : width of square
@param[in] height : height of square
*/
void ssd1306_clear_square(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
/**
@brief draw filled square at given position with given size
@param[in] p : instance of display
@param[in] x : x position of starting point
@param[in] y : y position of starting point
@param[in] width : width of square
@param[in] height : height of square
*/
void ssd1306_draw_square(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
/**
@brief draw empty square at given position with given size
@param[in] p : instance of display
@param[in] x : x position of starting point
@param[in] y : y position of starting point
@param[in] width : width of square
@param[in] height : height of square
*/
void ssd1306_draw_empty_square(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
/**
@brief draw monochrome bitmap with offset
@param[in] p : instance of display
@param[in] data : image data (whole file)
@param[in] size : size of image data in bytes
@param[in] x_offset : offset of horizontal coordinate
@param[in] y_offset : offset of vertical coordinate
*/
void ssd1306_bmp_show_image_with_offset(ssd1306_t *p, const uint8_t *data, const long size, uint32_t x_offset, uint32_t y_offset);
/**
@brief draw monochrome bitmap
@param[in] p : instance of display
@param[in] data : image data (whole file)
@param[in] size : size of image data in bytes
*/
void ssd1306_bmp_show_image(ssd1306_t *p, const uint8_t *data, const long size);
/**
@brief draw char with given font
@param[in] p : instance of display
@param[in] x : x starting position of char
@param[in] y : y starting position of char
@param[in] scale : scale font to n times of original size (default should be 1)
@param[in] font : pointer to font
@param[in] c : character to draw
*/
void ssd1306_draw_char_with_font(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t scale, const uint8_t *font, char c);
/**
@brief draw char with builtin font
@param[in] p : instance of display
@param[in] x : x starting position of char
@param[in] y : y starting position of char
@param[in] scale : scale font to n times of original size (default should be 1)
@param[in] c : character to draw
*/
void ssd1306_draw_char(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t scale, char c);
/**
@brief draw string with given font
@param[in] p : instance of display
@param[in] x : x starting position of text
@param[in] y : y starting position of text
@param[in] scale : scale font to n times of original size (default should be 1)
@param[in] font : pointer to font
@param[in] s : text to draw
*/
void ssd1306_draw_string_with_font(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t scale, const uint8_t *font, const char *s );
/**
@brief draw string with builtin font
@param[in] p : instance of display
@param[in] x : x starting position of text
@param[in] y : y starting position of text
@param[in] scale : scale font to n times of original size (default should be 1)
@param[in] s : text to draw
*/
void ssd1306_draw_string(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t scale, const char *s);
#endif

200
Code/src/io.c Normal file
View File

@ -0,0 +1,200 @@
#include "include/io.h"
#include <pico/stdlib.h>
#include <time.h>
#include <hardware/pwm.h>
#include "include/gyro.h"
void motor_control_activated(bool activated)
{
const uint SLICE_NUM = pwm_gpio_to_slice_num(PIN_MOTOR1);
pwm_set_enabled(SLICE_NUM, activated);
}
static void init_tirette_and_color(void)
{
gpio_init(PIN_TIRETTE);
gpio_init(PIN_BUTTON_COLOR);
gpio_set_dir(PIN_TIRETTE, GPIO_OUT);
gpio_set_dir(PIN_BUTTON_COLOR, GPIO_OUT);
gpio_pull_up(PIN_TIRETTE);
gpio_pull_up(PIN_BUTTON_COLOR);
}
static void init_motors(void)
{
gpio_set_function(PIN_MOTOR1, GPIO_FUNC_PWM);
gpio_set_function(PIN_MOTOR2, GPIO_FUNC_PWM);
const uint SLICE_NUM = pwm_gpio_to_slice_num(PIN_MOTOR1);
pwm_set_wrap(SLICE_NUM, 4096);
pwm_set_gpio_level(PIN_MOTOR1, 0);
pwm_set_gpio_level(PIN_MOTOR2, 0);
pwm_set_enabled(SLICE_NUM, false);
}
static void init_servo(void)
{
const uint SLICE_NUM = pwm_gpio_to_slice_num(PIN_SERVO);
gpio_set_function(PIN_SERVO, GPIO_FUNC_PWM);
pwm_set_wrap(SLICE_NUM, 25000);
pwm_set_clkdiv(SLICE_NUM, 100);
pwm_set_gpio_level(PIN_SERVO, 12500);
pwm_set_enabled(SLICE_NUM, false);
}
static void init_i2c(void)
{
gpio_set_function(I2C_MASTER_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_MASTER_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_MASTER_SDA_PIN);
gpio_pull_up(I2C_MASTER_SCL_PIN);
i2c_init(I2C_MASTER_INSTANCE, 100 * 1000);
}
static void init_screen(void)
{
io.screen->external_vcc=false;
ssd1306_init(io.screen, 128, 64, 0x3C, i2c1);
ssd1306_clear(io.screen);
ssd1306_show(io.screen);
}
void init_io(void)
{
init_tirette_and_color();
init_i2c();
init_gyro();
gyro_calibrate();
init_motors();
init_servo();
}
static void update_motion_control(void)
{
float actual_angle = io.gyro_data.z_angle;
float error = io.target_dir - actual_angle;
float correction = error * GAIN_KD;
int m1_speed = io.target_speed + correction;
int m2_speed = io.target_speed - correction;
if(m1_speed < 0)
{
m1_speed = 0;
}
else if(m2_speed < 0)
{
m2_speed = 0;
}
pwm_set_gpio_level(PIN_MOTOR1, m1_speed);
pwm_set_gpio_level(PIN_MOTOR2, m2_speed);
}
static void update_dancing_action(void)
{
static double prev_time = 0;
static double delta_time = 0;
static int actual_angle = 87;
static int8_t actual_dir = -1;
delta_time += io.time_ms - prev_time;
if(delta_time >= 10)
{
delta_time = 0;
actual_angle += actual_dir * DANCING_ACTION_DELTA_ANGLE;
}
if(actual_angle <= 43)
{
actual_dir = 1;
}
else if(actual_angle >= 130)
{
actual_dir = -1;
}
pwm_set_gpio_level(PIN_SERVO, actual_angle);
prev_time = io.time_ms;
}
void update_io(void)
{
io.time_ms = (double)clock() * 1000.0 / CLOCKS_PER_SEC;
static double last_color_change_time = 0;
static bool has_color_changed = true;
static bool is_color_blue = true;
if(io.is_color_blue != is_color_blue)
{
ssd1306_clear(io.screen);
if(is_color_blue)
{
ssd1306_draw_string(io.screen, 0, 20, 1, "Blue\nTeam");
}
else
{
ssd1306_draw_string(io.screen, 0, 20, 1, "Yellow\nTeam");
}
last_color_change_time = io.time_ms;
has_color_changed = true;
}
/*
if(io.time_ms - last_color_change_time > 1000 && has_color_changed)
{
ssd1306_clear(io.screen);
m_screen.drawBitmap(38, 0, Res::Imgs::riombotique, 52, 64, WHITE);
//m_screen.drawBitmap(75, 0, Imgs::poivron_robotique, 52, 64, WHITE);
//m_screen.drawBitmap(27, 0, Res::Imgs::diable_gaga, 75, 64, WHITE);
has_color_changed = false;
}
*/
io.is_tirette_pulled = gpio_get(PIN_TIRETTE);
io.is_color_blue = is_color_blue;
is_color_blue = !gpio_get(PIN_BUTTON_COLOR);
if(io.motion_control_activated)
{
update_motion_control();
}
if(io.is_dancing)
{
update_dancing_action();
}
ssd1306_show(io.screen);
}
void set_dir_with_angular_speed(float dir)
{}
void io_deinit(void)
{}

View File

@ -0,0 +1,145 @@
#include <pico/stdlib.h>
#include <stdio.h>
#include "include/io.h"
#define NORMAL_SPEED 2000.0f
#define TURNING_SPEED 1300.0f
#define ON_STAGE_SPEED 1100.0f
#define RAMPE_ANGLE -7.82907651006f
#define RAMPE_ANGLE_OFFSET 1.0f
#define RAMPE_START_ANGLE (RAMPE_ANGLE / 2 - RAMPE_ANGLE_OFFSET)
#define RAMPE_END_ANGLE (RAMPE_ANGLE / 2 + RAMPE_ANGLE_OFFSET)
// Unit tests activation
//#define UNIT_TESTS
/*
Etapes :
- 1ere etape : avancer jusqu'a la montée / condition : si montée detecté : etape suivante
- 2eme etape : avancer j'usqua la fin de la montée / condition : si fin de montée detecté : etape suivante
- 3eme etape : tourner de 90* / condition : si action terminé : etape suivante
- 4eme etape : avancer jusqu'au bors du plateau / condition : si choc de fin detecté : etape suivante
- 5eme etape : faire tourner actionneurs pour figurine
*/
/*
Modules necessaires :
- angle // Asservissement et control du robot
- choc // Controle du robot (fin !)
(?)- accelerometre // Asservissement
*/
// Movement states
typedef enum state_t {
WaitingForTirette,
WaitingTimer,
ForwardToRamp,
ForwardToScene,
Turn90Blue, // If blue team
Turn90Yellow, // If yellow team => this is the only action where you have to do something different depending on your team
ForwardToSceneEdge,
Dancing
} state_t;
io_t io;
void main(void)
{
stdio_init_all();
// Initialise IO
init_io();
motor_control_activated(true);
io.target_dir = 0.0f;
io.target_speed = 0.0f;
//my_IO.startDancingAction(15);
#ifdef UNIT_TESTS
Serial.println("UNIT_TESTS");
#endif
printf("Rampe Angle : %f\n", RAMPE_ANGLE);
printf("Rampe Angle Offset : %f\n", RAMPE_ANGLE_OFFSET);
printf("Rampe Start Angle : %f\n", RAMPE_START_ANGLE);
printf("Rampe End Angle : %f\n", RAMPE_END_ANGLE);
state_t actual_state = WaitingForTirette;
while(true)
{
update_io();
switch(actual_state)
{
case WaitingForTirette:
if(io.is_tirette_pulled)
{
actual_state = WaitingTimer;
}
break;
case WaitingTimer:
unsigned long initial_time = io.time_ms;
if(io.time_ms - initial_time >= 87000)
{
actual_state = ForwardToRamp;
}
break;
case ForwardToRamp:
motor_control_activated(true);
io.target_speed = NORMAL_SPEED;
if(io.gyro_data.y_angle < RAMPE_START_ANGLE)
{
actual_state = ForwardToScene;
}
break;
case ForwardToScene:
if(io.gyro_data.y_angle > RAMPE_END_ANGLE)
{
io.target_speed = TURNING_SPEED;
actual_state = io.is_color_blue ? Turn90Blue : Turn90Yellow;
}
break;
case Turn90Blue:
set_dir_with_angular_speed(90.0f);
if(io.gyro_data.z_angle >= 80.0f)
{
actual_state = ForwardToSceneEdge;
}
break;
case Turn90Yellow:
set_dir_with_angular_speed(-90.0f);
if(io.gyro_data.z_angle <= -80.0f)
{
actual_state = ForwardToSceneEdge;
}
break;
case ForwardToSceneEdge:
io.target_speed = ON_STAGE_SPEED;
if(io.gyro_data.y_angle > 3.0f)
{
actual_state = Dancing;
}
break;
case Dancing:
motor_control_activated(false);
io.target_speed = 0.0f;
io.is_dancing = true;
break;
}
}
}

306
Code/src/ssd1306.c Normal file
View File

@ -0,0 +1,306 @@
/*
MIT License
Copyright (c) 2021 David Schramm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pico/stdlib.h>
#include <hardware/i2c.h>
#include <pico/binary_info.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "include/ssd1306.h"
#include "include/font.h"
inline static void swap(int32_t *a, int32_t *b) {
int32_t *t=a;
*a=*b;
*b=*t;
}
inline static void fancy_write(i2c_inst_t *i2c, uint8_t addr, const uint8_t *src, size_t len, char *name) {
switch(i2c_write_blocking(i2c, addr, src, len, false)) {
case PICO_ERROR_GENERIC:
printf("[%s] addr not acknowledged!\n", name);
break;
case PICO_ERROR_TIMEOUT:
printf("[%s] timeout!\n", name);
break;
default:
//printf("[%s] wrote successfully %lu bytes!\n", name, len);
break;
}
}
inline static void ssd1306_write(ssd1306_t *p, uint8_t val) {
uint8_t d[2]= {0x00, val};
fancy_write(p->i2c_i, p->address, d, 2, "ssd1306_write");
}
bool ssd1306_init(ssd1306_t *p, uint16_t width, uint16_t height, uint8_t address, i2c_inst_t *i2c_instance) {
p->width=width;
p->height=height;
p->pages=height/8;
p->address=address;
p->i2c_i=i2c_instance;
p->bufsize=(p->pages)*(p->width);
if((p->buffer=malloc(p->bufsize+1))==NULL) {
p->bufsize=0;
return false;
}
++(p->buffer);
// from https://github.com/makerportal/rpi-pico-ssd1306
uint8_t cmds[]= {
SET_DISP,
// timing and driving scheme
SET_DISP_CLK_DIV,
0x80,
SET_MUX_RATIO,
height - 1,
SET_DISP_OFFSET,
0x00,
// resolution and layout
SET_DISP_START_LINE,
// charge pump
SET_CHARGE_PUMP,
p->external_vcc?0x10:0x14,
SET_SEG_REMAP | 0x01, // column addr 127 mapped to SEG0
SET_COM_OUT_DIR | 0x08, // scan from COM[N] to COM0
SET_COM_PIN_CFG,
width>2*height?0x02:0x12,
// display
SET_CONTRAST,
0xff,
SET_PRECHARGE,
p->external_vcc?0x22:0xF1,
SET_VCOM_DESEL,
0x30, // or 0x40?
SET_ENTIRE_ON, // output follows RAM contents
SET_NORM_INV, // not inverted
SET_DISP | 0x01,
// address setting
SET_MEM_ADDR,
0x00, // horizontal
};
for(size_t i=0; i<sizeof(cmds); ++i)
ssd1306_write(p, cmds[i]);
return true;
}
inline void ssd1306_deinit(ssd1306_t *p) {
free(p->buffer-1);
}
inline void ssd1306_poweroff(ssd1306_t *p) {
ssd1306_write(p, SET_DISP|0x00);
}
inline void ssd1306_poweron(ssd1306_t *p) {
ssd1306_write(p, SET_DISP|0x01);
}
inline void ssd1306_contrast(ssd1306_t *p, uint8_t val) {
ssd1306_write(p, SET_CONTRAST);
ssd1306_write(p, val);
}
inline void ssd1306_invert(ssd1306_t *p, uint8_t inv) {
ssd1306_write(p, SET_NORM_INV | (inv & 1));
}
inline void ssd1306_clear(ssd1306_t *p) {
memset(p->buffer, 0, p->bufsize);
}
void ssd1306_clear_pixel(ssd1306_t *p, uint32_t x, uint32_t y) {
if(x>=p->width || y>=p->height) return;
p->buffer[x+p->width*(y>>3)]&=~(0x1<<(y&0x07));
}
void ssd1306_draw_pixel(ssd1306_t *p, uint32_t x, uint32_t y) {
if(x>=p->width || y>=p->height) return;
p->buffer[x+p->width*(y>>3)]|=0x1<<(y&0x07); // y>>3==y/8 && y&0x7==y%8
}
void ssd1306_draw_line(ssd1306_t *p, int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
if(x1>x2) {
swap(&x1, &x2);
swap(&y1, &y2);
}
if(x1==x2) {
if(y1>y2)
swap(&y1, &y2);
for(int32_t i=y1; i<=y2; ++i)
ssd1306_draw_pixel(p, x1, i);
return;
}
float m=(float) (y2-y1) / (float) (x2-x1);
for(int32_t i=x1; i<=x2; ++i) {
float y=m*(float) (i-x1)+(float) y1;
ssd1306_draw_pixel(p, i, (uint32_t) y);
}
}
void ssd1306_clear_square(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
for(uint32_t i=0; i<width; ++i)
for(uint32_t j=0; j<height; ++j)
ssd1306_clear_pixel(p, x+i, y+j);
}
void ssd1306_draw_square(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
for(uint32_t i=0; i<width; ++i)
for(uint32_t j=0; j<height; ++j)
ssd1306_draw_pixel(p, x+i, y+j);
}
void ssd1306_draw_empty_square(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
ssd1306_draw_line(p, x, y, x+width, y);
ssd1306_draw_line(p, x, y+height, x+width, y+height);
ssd1306_draw_line(p, x, y, x, y+height);
ssd1306_draw_line(p, x+width, y, x+width, y+height);
}
void ssd1306_draw_char_with_font(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t scale, const uint8_t *font, char c) {
if(c<font[3]||c>font[4])
return;
uint32_t parts_per_line=(font[0]>>3)+((font[0]&7)>0);
for(uint8_t w=0; w<font[1]; ++w) { // width
uint32_t pp=(c-font[3])*font[1]*parts_per_line+w*parts_per_line+5;
for(uint32_t lp=0; lp<parts_per_line; ++lp) {
uint8_t line=font[pp];
for(int8_t j=0; j<8; ++j, line>>=1) {
if(line & 1)
ssd1306_draw_square(p, x+w*scale, y+((lp<<3)+j)*scale, scale, scale);
}
++pp;
}
}
}
void ssd1306_draw_string_with_font(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t scale, const uint8_t *font, const char *s) {
for(int32_t x_n=x; *s; x_n+=(font[1]+font[2])*scale) {
ssd1306_draw_char_with_font(p, x_n, y, scale, font, *(s++));
}
}
void ssd1306_draw_char(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t scale, char c) {
ssd1306_draw_char_with_font(p, x, y, scale, font_8x5, c);
}
void ssd1306_draw_string(ssd1306_t *p, uint32_t x, uint32_t y, uint32_t scale, const char *s) {
ssd1306_draw_string_with_font(p, x, y, scale, font_8x5, s);
}
static inline uint32_t ssd1306_bmp_get_val(const uint8_t *data, const size_t offset, uint8_t size) {
switch(size) {
case 1:
return data[offset];
case 2:
return data[offset]|(data[offset+1]<<8);
case 4:
return data[offset]|(data[offset+1]<<8)|(data[offset+2]<<16)|(data[offset+3]<<24);
default:
__builtin_unreachable();
}
__builtin_unreachable();
}
void ssd1306_bmp_show_image_with_offset(ssd1306_t *p, const uint8_t *data, const long size, uint32_t x_offset, uint32_t y_offset) {
if(size<54) // data smaller than header
return;
const uint32_t bfOffBits=ssd1306_bmp_get_val(data, 10, 4);
const uint32_t biSize=ssd1306_bmp_get_val(data, 14, 4);
const uint32_t biWidth=ssd1306_bmp_get_val(data, 18, 4);
const int32_t biHeight=(int32_t) ssd1306_bmp_get_val(data, 22, 4);
const uint16_t biBitCount=(uint16_t) ssd1306_bmp_get_val(data, 28, 2);
const uint32_t biCompression=ssd1306_bmp_get_val(data, 30, 4);
if(biBitCount!=1) // image not monochrome
return;
if(biCompression!=0) // image compressed
return;
const int table_start=14+biSize;
uint8_t color_val=0;
for(uint8_t i=0; i<2; ++i) {
if(!((data[table_start+i*4]<<16)|(data[table_start+i*4+1]<<8)|data[table_start+i*4+2])) {
color_val=i;
break;
}
}
uint32_t bytes_per_line=(biWidth/8)+(biWidth&7?1:0);
if(bytes_per_line&3)
bytes_per_line=(bytes_per_line^(bytes_per_line&3))+4;
const uint8_t *img_data=data+bfOffBits;
int32_t step=biHeight>0?-1:1;
int32_t border=biHeight>0?-1:-biHeight;
for(uint32_t y=biHeight>0?biHeight-1:0; y!=(uint32_t)border; y+=step) {
for(uint32_t x=0; x<biWidth; ++x) {
if(((img_data[x>>3]>>(7-(x&7)))&1)==color_val)
ssd1306_draw_pixel(p, x_offset+x, y_offset+y);
}
img_data+=bytes_per_line;
}
}
inline void ssd1306_bmp_show_image(ssd1306_t *p, const uint8_t *data, const long size) {
ssd1306_bmp_show_image_with_offset(p, data, size, 0, 0);
}
void ssd1306_show(ssd1306_t *p) {
uint8_t payload[]= {SET_COL_ADDR, 0, p->width-1, SET_PAGE_ADDR, 0, p->pages-1};
if(p->width==64) {
payload[1]+=32;
payload[2]+=32;
}
for(size_t i=0; i<sizeof(payload); ++i)
ssd1306_write(p, payload[i]);
*(p->buffer-1)=0x40;
fancy_write(p->i2c_i, p->address, p->buffer-1, p->bufsize+1, "ssd1306_show");
}