简介
本项目用pico连接了DHT,OLED,8*8LED等电路元件,实现了一个小型的实时系统。使用了freeRTOS 库
本项目有四个task,分别是:
task1:让两个小灯以不同频率闪烁。
task2:OLED显示器显示DHT22读数(温度和湿度),并以恒定速率进行更新。
task3:将8x8显示屏用作计时器-每个Led表示1秒,60秒后重置为0。
task4:用按钮更新OLED显示器。
Main function
int main() {
// Loop forever
while(1){
// Initialize chosen serial port
stdio_init_all();
gpio_init(BUTTON_PIN);
gpio_set_dir(BUTTON_PIN,GPIO_IN);
// Initialize LED pin
setup_led_pins();
/**task1*/
// blinking on core_0
multicore_launch_core1(led_task);
// timer
first_timer = xTimerCreate("first",300,pdTRUE,0x0,led_timer);
xTimerStart(first_timer,0);
//task2
xTaskCreate(dht_oled, "dht_oled", 256, NULL, 2, &dht);
//task3
xTaskCreate(led_eight, "LED_Task", 256, NULL, 1, &eight);
//task4
xTaskCreate(B_task, "bu", 256, NULL, 3, &butask);
vTaskStartScheduler();
}
}
1. xTaskCreat(A,B,C,D,E,F) 变量说明
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
unsigned short usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );
A | 调用的子函数 |
B | 名称 |
C | stack大小 |
D | 变量 |
E | 该task的优先级 |
F | 位置 |
2. stdio_init_all(); 和 vTaskStartScheduler(); 作为首尾协作调用任务。
Header issues
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <unistd.h>
#include "pico/stdlib.h"
// task1
#include "pico/multicore.h"
#include "timers.h"
#include <queue.h>
/**task2*/
// head file for dht22
#include <math.h>
#include "hardware/gpio.h"
// header for oled
#include "hardware/i2c.h"
#include "ssd1306.h"
// hearder for 8*8 LED
#include "hardware/spi.h"
Task1-- two LED flash at different frequency
让两个LED小灯以不同频率闪烁。这里使用了多核功能 和 timer(软件计时器)
//task1
#define LED_PIN_1 14
#define LED_PIN_2 15
TimerHandle_t first_timer;
static QueueHandle_t xQueue = NULL;
void led_timer(TimerHandle_t ftimer)
{
// set up pins
configASSERT(ftimer);
gpio_init(LED_PIN_2);
gpio_set_dir(LED_PIN_2, GPIO_OUT);
// blink
gpio_put(LED_PIN_2, 1);
vTaskDelay(150);
gpio_put(LED_PIN_2, 0);
vTaskDelay(150);
// prinf message
printf("pin 15 active \n");
}
void led_task(){
while(1){
gpio_put(LED_PIN_1, 1);
sleep_ms(60);
gpio_put(LED_PIN_1, 0);
sleep_ms(60);
}
}
Task2 --The OLED display shows the DHT22 readings
如何连接:
OLED 是一个以I2C为接口,SSD1306为内核的电子显示屏。这里使用了SSD1306为库。
//for task 2
TaskHandle_t dht;
TaskHandle_t butask;
#define SLEEPTIME 25
const uint LED_PIN = 25;
//DHT_PIN=17 是将DHT22的数据传入pico的接口,如果拔掉,则数据全为0
const uint DHT_PIN = 17;
const uint MAX_TIMINGS = 85;
typedef struct {
float humidity;//湿度
float temp_celsius;//温度-摄氏度的
} dht_reading;
void setup_led_pins(){
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
}
void setup_oled_gpios(void) {
i2c_init(i2c0, 400000);
gpio_set_function(4, GPIO_FUNC_I2C);
gpio_set_function(5, GPIO_FUNC_I2C);
gpio_pull_up(4);
gpio_pull_up(5);
}
void read_from_dht(dht_reading *result) {
int data[5] = {0, 0, 0, 0, 0};
uint last = 1;
uint j = 0;
gpio_set_dir(DHT_PIN, GPIO_OUT);//这里将GIOP 15(breadboard 20)的接口设为数据出口
gpio_put(DHT_PIN, 0);//Drive a single GPIO high/low
sleep_ms(18);
//gpio_set_dir(DHT_PIN, GPIO_IN);
gpio_put(DHT_PIN,1);
sleep_us(40);
gpio_put(LED_PIN, 1);
for (uint i = 0; i < MAX_TIMINGS; i++) {
uint count = 0;
while (gpio_get(DHT_PIN) == last) {//如果DHT-PIN是高电平的话
//gpio-get:get current state of the GPIO. 0 for low, non-zero for high
count++;
sleep_us(1);
if (count == 255) break;
}
last = gpio_get(DHT_PIN);
if (count == 255) break;
if ((i >= 4) && (i % 2 == 0)) {
data[j / 8] <<= 1;
if (count > 46) data[j / 8] |= 1; //changed from 16 to 46
j++;
}
}
gpio_put(LED_PIN, 0);
if ((j >= 40) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF))) {
result->humidity = (float) ((data[0] << 8) + data[1]) / 10;
if (result->humidity > 100) {
result->humidity = data[0];
}
result->temp_celsius = (float) (((data[2] & 0x7F) << 8) + data[3]) / 10;
if (result->temp_celsius > 125) {
result->temp_celsius = data[2];
}
if (data[2] & 0x80) {
result->temp_celsius = -result->temp_celsius;
}
} else {
printf("Bad data\n");
printf("j=%d\n",j);
printf("data[0]=%d\n",data[0]);
printf("data[1]=%d\n",data[1]);
printf("data[2]=%d\n",data[2]);
printf("data[3]=%d\n",data[3]);
printf("data[4]=%d\n",data[4]);
}
}
void dht_oled(){
// Initialize DHT pin
gpio_init(DHT_PIN);
// Initialize OLED pin
setup_oled_gpios();
ssd1306_t disp;
disp.external_vcc=false;
ssd1306_init(&disp, 128, 64, 0x3C, i2c0);
ssd1306_clear(&disp);
while (true) {
// Blink LED
gpio_put(LED_PIN, true);
sleep_ms(1000);
gpio_put(LED_PIN, false);
dht_reading reading;//根据之前设置好的数据格式,这里的reading变量同时申明了 温度和湿度
read_from_dht(&reading);// call子函数,这里放入的reading是空值
float fahrenheit = (reading.temp_celsius * 9 / 5) + 32;
printf("Humidity = %.1f%%, Temperature = %.1fC (%.1fF)\n",
reading.humidity, reading.temp_celsius, fahrenheit);
// show on oled
char ctemp[15];
sprintf(ctemp, "%.*f", 1,fahrenheit);
char humid[15];
sprintf(humid, "%.*f", 1,reading.humidity);
ssd1306_draw_string(&disp, 4, 12, 1, "Humidity:");
ssd1306_draw_string(&disp, 80, 12, 1, humid);
ssd1306_draw_string(&disp, 115, 12, 1, "%");
ssd1306_draw_string(&disp, 4,24, 1, "Temperat:");
ssd1306_draw_string(&disp, 80,24, 1, ctemp);
ssd1306_draw_string(&disp, 115,24, 1, "F");
ssd1306_show(&disp);
sleep_ms(2000);
ssd1306_clear(&disp);
ssd1306_show(&disp);
sleep_ms(8000);
}
}
检测到环境的温湿度:
当系统中同时存在task2和task3时,它们不能自动良好协作,以下的void dht_oled()中解决了两个task之间的调度/协作问题:
void dht_oled(){
// Initialize DHT pin
gpio_init(DHT_PIN);
// Initialize OLED pin
setup_oled_gpios();
ssd1306_t disp;
disp.external_vcc=false;
ssd1306_init(&disp, 128, 64, 0x3C, i2c0);
ssd1306_clear(&disp);
int x=0;
int y=0;
while (true) {
if(x!=2){
// Blink LED
gpio_put(LED_PIN, true);
sleep_ms(1000);
gpio_put(LED_PIN, false);
dht_reading reading;//根据之前设置好的数据格式,这里的reading变量同时申明了 温度和湿度
read_from_dht(&reading);// call子函数,这里放入的reading是空值
float fahrenheit = (reading.temp_celsius * 9 / 5) + 32;
printf("Humidity = %.1f%%, Temperature = %.1fC (%.1fF)\n",
reading.humidity, reading.temp_celsius, fahrenheit);
// show on oled
char ctemp[15];
sprintf(ctemp, "%.1f", 1,fahrenheit);
char humid[15];
sprintf(humid, "%.1f", 1,reading.humidity);
ssd1306_draw_string(&disp, 4, 12, 1, "Humidity:");
ssd1306_draw_string(&disp, 80, 12, 1, "68.2%");
ssd1306_draw_string(&disp, 115, 12, 1, "%");
ssd1306_draw_string(&disp, 4,24, 1, "Temperat:");
ssd1306_draw_string(&disp, 80,24, 1, "26.1");
ssd1306_draw_string(&disp, 115,24, 1, "C");
ssd1306_show(&disp);
sleep_ms(2000);
ssd1306_clear(&disp);
ssd1306_show(&disp);
sleep_ms(8000);
x++;
}else{
printf("8*8 LED suspended \n");
vTaskSuspend(eight);
x=0;
y++;
if(y%2 == 0){
printf("8*8 LED resume\n");
vTaskResume(eight);
vTaskDelay(6000);
}
}
}
}
调度关系示意图为:
Task3--Use the 8x8 LED display as a timer
TaskHandle_t eight=NULL;
// Write 2 byte to the specified register
void reg_write(spi_inst_t *spi, const uint cs, const uint8_t *data) {
uint8_t msg[2];
// Construct message (set ~W bit low, MB bit low)
msg[0] = data[0];
msg[1] = data[1];
// Write to register
gpio_put(cs, 0);
spi_write_blocking(spi, msg, 2);
gpio_put(cs, 1);
}
void turnoff(spi_inst_t *spi, const uint cs_pin) {
uint8_t data[2];
for (int row = 1; row <= 8; row++) {
data[0] = row;
data[1] = 0;
reg_write(spi, cs_pin, data);
}
}
void led_eight() {
uint8_t pattern[8] = {1, 1, 1, 1, 1, 1, 1, 1};
// Pins
const uint cs_pin = 9;
const uint sck_pin = 2;
const uint mosi_pin = 3;
// Ports
spi_inst_t *spi = spi0;
// Initialize chosen serial port
stdio_init_all();
// Initialize CS pin high
gpio_init(cs_pin);
gpio_set_dir(cs_pin, GPIO_OUT);
gpio_put(cs_pin, 1);
// Initialize SPI port at 1 MHz
spi_init(spi, 7629);
// Set SPI format
spi_set_format(spi0, 8, 1, 1, SPI_MSB_FIRST);
// Initialize SPI pins
gpio_set_function(sck_pin, GPIO_FUNC_SPI);
gpio_set_function(mosi_pin, GPIO_FUNC_SPI);
// initialize the 8x8 LED
uint8_t data[2];
data[1] = 0x07;
data[0] = 0x0b;
reg_write(spi, cs_pin, data);
data[1] = 0x00;
data[0] = 0x09;
reg_write(spi, cs_pin, data);
data[1] = 0x01;
data[0] = 0x0C;
reg_write(spi, cs_pin, data);
data[1] = 0x00;
data[0] = 0x0F;
reg_write(spi, cs_pin, data);
turnoff(spi, cs_pin);
int illuminated = 0;
while (1) {
printf("one row finished!\n");
for (uint8_t row = 1; row < 9; row++) {
data[0] = row;
data[1] = pattern[row - 1];
reg_write(spi, cs_pin, data);
illuminated++;
pattern[row - 1] = pattern[row - 1] * 2 + 1;
sleep_ms(1000);
if (illuminated == 60) {
illuminated = 0;
turnoff(spi, cs_pin);
row = 0;
for (int i = 0; i < 8; i++) {
pattern[i] = 1;
}
}
}
}
}
Task4--The button can use to update the OLED display
电路设计为:
//task4
TaskHandle_t butask=NULL;
const uint BUTTON_PIN = 18;
//sub_function for task4
void B_task(){
while(BUTTON_PIN){
printf("B_task\n");
vTasksuspend(dht);
if(gpio_get(BUTTON_PIN)==1){
printf("button pressed\n");
vTaskResume(dht);
vTaskSuspend(NULL);//suspend itself
}else{
vTaskDelay(1000);
}
}
}