esp32 idf记录(二)

上一篇文章基本上手了下,这篇文章记录下外设的基本使用

文章见:
esp32 idf记录(一)

另外有一些重要参考资料:

ESP32编程指南
CSDN博客

1、GPIO

关于gpio,需要说明一下:
在这里插入图片描述

gpio驱动可以使用两种方式,一种是库函数,一种是像stm32一样设置模式,下面是示例程序:

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#include "driver/gpio.h"

#define LED_PORT GPIO_NUM_2
#define KEY_PORT GPIO_NUM_0
// #define LED_USE_SIMPLE
// #define KEY_USE_SIMPLE

void app_main(void)
{
#ifdef LED_USE_SIMPLE
	esp_rom_gpio_pad_select_gpio(LED_PORT);
	gpio_set_direction(LED_PORT, GPIO_MODE_DEF_OUTPUT);
#else
	gpio_config_t ledcfg = {
		.pin_bit_mask = (1ull << LED_PORT),
		.mode = GPIO_MODE_OUTPUT,
		.pull_up_en = 0,
		.pull_down_en = 1,
		.intr_type = 0,
	};

	gpio_config(&ledcfg);
#endif

#ifdef KEY_USE_SIMPLE
	esp_rom_gpio_pad_select_gpio(KEY_PORT);
	gpio_set_direction(KEY_PORT, GPIO_MODE_INPUT);
#else
	gpio_config_t keycfg = {
		.intr_type = GPIO_INTR_DISABLE, /*关中断*/
		.mode = GPIO_MODE_INPUT,
		.pin_bit_mask = (1ull << KEY_PORT),
		.pull_down_en = 0, /*禁止下拉*/
		.pull_up_en = 0,   /*禁止上拉*/
	};

	gpio_config(&keycfg);
#endif
	while (1)
	{
		printf("Turning off the LED\n");
		gpio_set_level(LED_PORT, 0);
		vTaskDelay(1000 / portTICK_PERIOD_MS);

		printf("Turning on the LED\n");
		gpio_set_level(LED_PORT, 1);
		vTaskDelay(1000 / portTICK_PERIOD_MS);

		/*输出引脚电平*/
		printf("key value %d\n", gpio_get_level(KEY_PORT));

	}
}

上面的程序包含了设置gpio输出和输入,即通用的led和按键的功能。使用库函数的方式设置gpio输入输出如下所示:

设置输出

	esp_rom_gpio_pad_select_gpio(LED_PORT);
	gpio_set_direction(LED_PORT, GPIO_MODE_DEF_OUTPUT);

设置输入

	esp_rom_gpio_pad_select_gpio(KEY_PORT);
	gpio_set_direction(KEY_PORT, GPIO_MODE_INPUT);

如果使用gpio_init设置,如下所示
设置输出

	gpio_config_t ledcfg = {
		.pin_bit_mask = (1ull << LED_PORT),
		.mode = GPIO_MODE_OUTPUT,
		.pull_up_en = 0,
		.pull_down_en = 1,
		.intr_type = 0,
	};

	gpio_config(&ledcfg);

设置输入

	gpio_config_t keycfg = {
		.intr_type = GPIO_INTR_DISABLE, /*关中断*/
		.mode = GPIO_MODE_INPUT,
		.pin_bit_mask = (1ull << KEY_PORT),
		.pull_down_en = 0, /*禁止下拉*/
		.pull_up_en = 0,   /*禁止上拉*/
	};

	gpio_config(&keycfg);

上面的方式读取io口使用轮询来实现的,同样可以使用中断,下面是中断模式的示例,点击可以跳转到中断的位置:
在这里插入图片描述
中断模式设置如下:
在这里插入图片描述
结合原理图,应该需要设置为下降沿触发
在这里插入图片描述
之后就是设置中断服务函数了中断执行逻辑了

	/*注册中断服务*/
	gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3);
	/*设置中断的服务函数*/
	gpio_isr_handler_add(KEY_PORT, gpio_isr_handler, (void*) KEY_PORT);

中断服务函数里面执行一个简单的逻辑

static int level = 0;

void IRAM_ATTR gpio_isr_handler(void* arg) 
{
	level++;
	if(level == 2)
	{
		level = 0;
	}
	gpio_set_level(LED_PORT, level%2);
}

完整代码如下,实现按键按一下改变一次led状态

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#include "driver/gpio.h"

#define LED_PORT GPIO_NUM_2
#define KEY_PORT GPIO_NUM_0
// #define LED_USE_SIMPLE
// #define KEY_USE_SIMPLE

static int level = 0;

void IRAM_ATTR gpio_isr_handler(void* arg) 
{
	level++;
	if(level == 2)
	{
		level = 0;
	}
	gpio_set_level(LED_PORT, level%2);
}

void app_main(void)
{
#ifdef LED_USE_SIMPLE
	esp_rom_gpio_pad_select_gpio(LED_PORT);
	gpio_set_direction(LED_PORT, GPIO_MODE_DEF_OUTPUT);
#else
	gpio_config_t ledcfg = {
		.pin_bit_mask = (1ull << LED_PORT),
		.mode = GPIO_MODE_OUTPUT,
		.pull_up_en = 0,
		.pull_down_en = 1,
		.intr_type = 0,
	};

	gpio_config(&ledcfg);
#endif

#ifdef KEY_USE_SIMPLE
	esp_rom_gpio_pad_select_gpio(KEY_PORT);
	gpio_set_direction(KEY_PORT, GPIO_MODE_INPUT);
	/*加入中断配置*/
	gpio_intr_enable(KEY_PORT);
	gpio_set_intr_type(KEY_PORT, GPIO_INTR_NEGEDGE);
	gpio_pullup_en(KEY_PORT);
#else
	gpio_config_t keycfg = {
		.intr_type = GPIO_INTR_NEGEDGE, /*关中断*/
		.mode = GPIO_MODE_INPUT,
		.pin_bit_mask = (1ull << KEY_PORT),
		.pull_down_en = GPIO_PULLDOWN_DISABLE, /*禁止下拉*/
		.pull_up_en = GPIO_PULLDOWN_ENABLE,   /*设置上拉*/
	};

	gpio_config(&keycfg);
#endif
	/*注册中断服务*/
	gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3);
	/*设置中断的服务函数*/
	gpio_isr_handler_add(KEY_PORT, gpio_isr_handler, (void*) KEY_PORT);

	while (1)
	{
		printf("Turning off the LED\n");
		// gpio_set_level(LED_PORT, 0);
		vTaskDelay(1000 / portTICK_PERIOD_MS);

		printf("Turning on the LED\n");
		// gpio_set_level(LED_PORT, 1);
		vTaskDelay(1000 / portTICK_PERIOD_MS);

		/*输出引脚电平*/
		// printf("key value %d\n", gpio_get_level(KEY_PORT));

	}
}

需要注意这个中断回调函数是个异步操作,在这里面不能做函数调用,例如加入printf都会导致程序错误。

2、定时器

2.1硬件定时器

esp32的定时器描述如下:
在这里插入图片描述
但是我再用的时候发现有两种定时器,说实话这两种有啥区别我也没看出来,区别就是一种的头文件是#include "driver/gptimer.h",一种是#include "driver/timer.h"

下面是使用#include "driver/gptimer.h"的实现

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gptimer.h"
#include "driver/gpio.h"

#define LED_PORT GPIO_NUM_2
static int led_value = 0;

static bool IRAM_ATTR example_timer_on_alarm_cb_v1(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
	// printf("run here \r\n");
	led_value++;
	if(led_value == 2)
	{
		led_value = 0;
	}
	gpio_set_level(LED_PORT, led_value%2);

	return 0;
}

void app_main(void)
{
	esp_rom_gpio_pad_select_gpio(LED_PORT);
	gpio_set_direction(LED_PORT, GPIO_MODE_DEF_OUTPUT);

    gptimer_handle_t gptimer = NULL;
    gptimer_config_t timer_config = {
        .clk_src = GPTIMER_CLK_SRC_DEFAULT,
        .direction = GPTIMER_COUNT_UP,
        .resolution_hz = 1000000, // 1MHz, 1 tick=1us
    };

	gptimer_new_timer(&timer_config, &gptimer);

    gptimer_event_callbacks_t cbs = {
        .on_alarm = example_timer_on_alarm_cb_v1,
    };

	gptimer_register_event_callbacks(gptimer, &cbs, NULL);

	gptimer_enable(gptimer);

    gptimer_alarm_config_t alarm_config1 = {
        .reload_count = 0,
        .alarm_count = 1000000, // period = 1s
        .flags.auto_reload_on_alarm = true,
    };
	gptimer_set_alarm_action(gptimer, &alarm_config1);
	gptimer_start(gptimer);

	gptimer_set_raw_count(gptimer, 100);
	uint64_t count;
	gptimer_get_raw_count(gptimer, &count);

	// gptimer_disable(gptimer);

	while (1)
	{
		printf("run here \r\n");
		vTaskDelay(1000 / portTICK_PERIOD_MS);
	}
}

上面的代码是实现了设置定时器频率为1mhz,到期后自动重装载,计数值为100000,这样就是1s一个周期,然后led触发一次就会闪烁一次。

这个代码参考:
https://github.com/espressif/esp-idf/blob/636ff35b52f10e1a804a3760a5bd94e68f4b1b71/components/esp_timer/src/esp_timer.c

源代码是实现了三种功能的,一种是简单定时,一种是自动重装载,一种是动态修改装载值。

下面是使用#include "driver/timer.h"的实现

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/timer.h"
#include "driver/gpio.h"

#define TIMER_DIVIDER 16
#define TIMER_BASE_CLK 80000000
#define TIMER_FREQ (TIMER_BASE_CLK / TIMER_DIVIDER) // 定时器计数频率

#define LED_PORT GPIO_NUM_2
static int led_value = 0;

#define USE_ISR

static bool timer_callback(void *args)
{
	led_value++;
	if (led_value == 2)
	{
		led_value = 0;
	}
	gpio_set_level(LED_PORT, led_value % 2);
	return 0;
}

static void IRAM_ATTR timerIsr(void *arg)
{
	led_value++;
	if (led_value == 2)
	{
		led_value = 0;
	}
	gpio_set_level(LED_PORT, led_value % 2);

    timer_group_clr_intr_status_in_isr(0, 0);
    timer_group_enable_alarm_in_isr(0, 0);
}

void app_main(void)
{
	esp_rom_gpio_pad_select_gpio(LED_PORT);
	gpio_set_direction(LED_PORT, GPIO_MODE_DEF_OUTPUT);

	timer_config_t config = {
		.divider = TIMER_DIVIDER,
		.counter_dir = TIMER_COUNT_UP,
		.counter_en = TIMER_PAUSE,
		.alarm_en = TIMER_ALARM_EN,
		.auto_reload = TIMER_AUTORELOAD_EN,
	};

	timer_init(0, 0, &config);
	timer_set_counter_value(0, 0, 0x00ull);
	timer_set_alarm_value(0, 0, TIMER_FREQ * 1);
	timer_enable_intr(0, 0);
#ifndef USE_ISR
	timer_isr_callback_add(0, 0, timer_callback, NULL, ESP_INTR_FLAG_IRAM);
#else
	timer_isr_register(0, 0, timerIsr, &config, ESP_INTR_FLAG_IRAM, NULL);
#endif	
	timer_start(0, 0);
	printf("定时器启动成功!");
}

上面的代码我是参考这个大佬写的:
https://blog.csdn.net/m0_50064262/article/details/115407884

但是后面发现他这个代码是4.4版本的库,现在我用的是5.1的,这两者之间不兼容,为啥呢,因为在编程指南里面已经看不到了
在这里插入图片描述
换到4.4版本的立马就有了
在这里插入图片描述
这个大佬的文章里也说明了这一点
在这里插入图片描述

2.2软件定时器

这是一个系统api,参考链接如下:
https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/system/esp_timer.html#high-resolution-timer
下面是示例代码,功能同样是led状态1s变化一次:

#include <stdio.h>
#include "esp_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_timer.h"

esp_timer_handle_t fw_timer_handle = 0;
#define LED_PORT GPIO_NUM_2
static int led_value = 0;

void fw_timer_cb(void *arg)
{
	led_value++;
	if (led_value == 2)
	{
		led_value = 0;
	}
	gpio_set_level(LED_PORT, led_value % 2);
}

void app_main()
{
	esp_rom_gpio_pad_select_gpio(LED_PORT);
	gpio_set_direction(LED_PORT, GPIO_MODE_DEF_OUTPUT);

	esp_timer_create_args_t fw_timer =
		{
			.callback = &fw_timer_cb, // 回调函数
			.arg = NULL,			  // 参数
			.name = "timer1"		  // 定时器名称
		};

	esp_err_t err = esp_timer_create(&fw_timer, &fw_timer_handle);
	err = esp_timer_start_periodic(fw_timer_handle, 1000 * 1000); // 1秒回调
	/*如果只需要执行一次*/
	//err = esp_timer_start_once(fw_timer_handle, 1000 * 1000);
	if (err == ESP_OK)
	{
		printf("timer1 cteate and start ok!\r\n");
	}
}

这里定时器的启动有两个api,分别是周期执行还是只执行一次,说明如下:
在这里插入图片描述

如果要停止定时器,需要下面这两个函数:
在这里插入图片描述

3、串口

esp32的串口外设配置如下:
在这里插入图片描述
从原理图上看串口的配置如下所示:
在这里插入图片描述

下面是一个串口回显功能配置

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"

#define ECHO_TEST_TXD (GPIO_NUM_1)
#define ECHO_TEST_RXD (GPIO_NUM_3)
#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)
#define ECHO_TEST_CTS (UART_PIN_NO_CHANGE)

#define BUF_SIZE (1024)

static void echo_task(void *arg)
{
	uart_config_t uart_config = {
		.baud_rate = 115200,
		.data_bits = UART_DATA_8_BITS,
		.parity = UART_PARITY_DISABLE,
		.stop_bits = UART_STOP_BITS_1,
		.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
		.source_clk = UART_SCLK_APB,
	};
	uart_driver_install(UART_NUM_0, BUF_SIZE * 2, 0, 0, NULL, 0);
	uart_param_config(UART_NUM_0, &uart_config);
	uart_set_pin(UART_NUM_0, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS);

	uint8_t *data = (uint8_t *)malloc(BUF_SIZE);

	while (1)
	{
		int len = uart_read_bytes(UART_NUM_0, data, BUF_SIZE, 20 / portTICK_PERIOD_MS);
		uart_write_bytes(UART_NUM_0, (const char *)data, len);
	}
}

void app_main(void)
{
	xTaskCreate(echo_task, "uart_echo_task", 1024, NULL, 10, NULL);
}

效果如下:
在这里插入图片描述
提示:
上面的示例如果要想达到示例的效果,就需要再发送的时候设置成发送新行,也就是加上\r\n的结尾符。
这里还有一种可以配置成模式检测的

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

#define BUF_SIZE (256)
#define RD_BUF_SIZE (BUF_SIZE)

uart_event_t uart0_event, uart1_event, uart2_event;
QueueHandle_t uart0_queue, uart1_queue, uart2_queue;

void uart_event_handle(uart_port_t uart_num, QueueHandle_t queue, uart_event_t *event);

void app_main(void)
{
	uart_config_t uart_config =
		{
			.baud_rate = 115200,
			.data_bits = UART_DATA_8_BITS,
			.parity = UART_PARITY_DISABLE,
			.stop_bits = UART_STOP_BITS_1,
			.flow_ctrl = UART_HW_FLOWCTRL_DISABLE};

	uart_param_config(UART_NUM_0, &uart_config);
	uart_param_config(UART_NUM_1, &uart_config);
	uart_param_config(UART_NUM_2, &uart_config);

	// uart_enable_intr_mask(UART_NUM_0, UART_DATA);

	uart_set_pin(UART_NUM_0, GPIO_NUM_1, GPIO_NUM_3, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
	uart_set_pin(UART_NUM_1, GPIO_NUM_18, GPIO_NUM_19, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
	uart_set_pin(UART_NUM_2, GPIO_NUM_16, GPIO_NUM_17, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

	uart_driver_install(UART_NUM_0, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);
	uart_driver_install(UART_NUM_1, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart1_queue, 0);
	uart_driver_install(UART_NUM_2, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart2_queue, 0);

	uart_pattern_queue_reset(UART_NUM_0, 20);
	uart_pattern_queue_reset(UART_NUM_1, 20);
	uart_pattern_queue_reset(UART_NUM_2, 20);

	while (1)
	{
		if (xQueueReceive(uart0_queue, (uint32_t *)&uart0_event, 0))
		{
			uart_event_handle(UART_NUM_0, uart0_queue, &uart0_event);
		}
		if (xQueueReceive(uart1_queue, (uint32_t *)&uart1_event, 0))
		{
			uart_event_handle(UART_NUM_1, uart1_queue, &uart1_event);
		}
		if (xQueueReceive(uart2_queue, (uint32_t *)&uart2_event, 0))
		{
			uart_event_handle(UART_NUM_2, uart2_queue, &uart2_event);
		}

		vTaskDelay(pdMS_TO_TICKS(10)); // Delay 10ms
	}
}

void uart_event_handle(uart_port_t uart_num, QueueHandle_t queue, uart_event_t *event)
{
	uint8_t *dtmp = malloc(RD_BUF_SIZE);
	bzero(dtmp, RD_BUF_SIZE);

	switch (event->type)
	{
	// Event of UART receving data
	/*We'd better handler data event fast, there would be much more data events than
	other types of events. If we take too much time on data event, the queue might
	be full.*/
	case UART_DATA:
		uart_read_bytes(uart_num, dtmp, event->size, portMAX_DELAY);
		uart_write_bytes(uart_num, (const char *)dtmp, event->size);
		break;

	// Event of HW FIFO overflow detected
	case UART_FIFO_OVF:
		// If fifo overflow happened, you should consider adding flow control for your application.
		// The ISR has already reset the rx FIFO,
		// As an example, we directly flush the rx buffer here in order to read more data.
		uart_flush_input(uart_num);
		xQueueReset(queue);
		break;

	// Event of UART ring buffer full
	case UART_BUFFER_FULL:
		// If buffer full happened, you should consider encreasing your buffer size
		// As an example, we directly flush the rx buffer here in order to read more data.
		uart_flush_input(uart_num);
		xQueueReset(queue);
		break;

	// Event of UART RX break detected
	case UART_BREAK:
		break;

	// Event of UART parity check error
	case UART_PARITY_ERR:
		break;

	// Event of UART frame error
	case UART_FRAME_ERR:
		break;

	// UART_PATTERN_DET
	case UART_PATTERN_DET:
		break;

	// Others
	default:
		break;
	}

	free(dtmp);
	dtmp = NULL;
}

最后都配置到了这里执行:
在这里插入图片描述
中断使用?
这个我尝试了,但是一直没成功,官方文档是这么写的
在这里插入图片描述
配置上如下,我是想配一个接收中断的
在这里插入图片描述
但是实际运行起来串口一直刷,应该是内部错误了

实测这个串口还支持类似linux的select功能,这个官方有提供例程

#include <stdio.h>
#include <sys/fcntl.h>
#include <sys/errno.h>
#include <sys/unistd.h>
#include <sys/select.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"

static const char* TAG = "uart_select_example";

static void uart_select_task(void *arg)
{
    if (uart_driver_install(UART_NUM_0, 2*1024, 0, 0, NULL, 0) != ESP_OK) {
        ESP_LOGE(TAG, "Driver installation failed");
        vTaskDelete(NULL);
    }

    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };

    uart_param_config(UART_NUM_0, &uart_config);

    while (1) {
        int fd;

        if ((fd = open("/dev/uart/0", O_RDWR)) == -1) {
            ESP_LOGE(TAG, "Cannot open UART");
            vTaskDelay(5000 / portTICK_PERIOD_MS);
            continue;
        }

        // We have a driver now installed so set up the read/write functions to use driver also.
        esp_vfs_dev_uart_use_driver(0);

        while (1) {
            int s;
            fd_set rfds;
            struct timeval tv = {
                .tv_sec = 5,
                .tv_usec = 0,
            };

            FD_ZERO(&rfds);
            FD_SET(fd, &rfds);

            s = select(fd + 1, &rfds, NULL, NULL, &tv);

            if (s < 0) {
                ESP_LOGE(TAG, "Select failed: errno %d", errno);
                break;
            } else if (s == 0) {
                ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");
            } else {
                if (FD_ISSET(fd, &rfds)) {
                    char buf;
                    if (read(fd, &buf, 1) > 0) {
                        ESP_LOGI(TAG, "Received: %c", buf);
                        // Note: Only one character was read even the buffer contains more. The other characters will
                        // be read one-by-one by subsequent calls to select() which will then return immediately
                        // without timeout.
                    } else {
                        ESP_LOGE(TAG, "UART read error");
                        break;
                    }
                } else {
                    ESP_LOGE(TAG, "No FD has been set in select()");
                    break;
                }
            }
        }

        close(fd);
    }

    vTaskDelete(NULL);
}

void app_main(void)
{
    xTaskCreate(uart_select_task, "uart_select_task", 4*1024, NULL, 5, NULL);
}

运行效果如下:
在这里插入图片描述
官方例程只能接收单个字符,这里小改一下接收完整字符串
修改这部分接收的逻辑如下

				if (FD_ISSET(fd, &rfds))
				{
					char c;
					if (read(fd, &c, 1) > 0)
					{
						if (c == '\n')
						{
							buf[buf_index] = '\0'; // 在字符串末尾添加 null 终止符
							ESP_LOGI(TAG, "Received: %s", buf);
							buf_index = 0;
							break; // 接收到换行符,退出循环
						}
						else
						{
							buf[buf_index++] = c; // 将字符存储到缓冲区中
						}
					}
					else
					{
						ESP_LOGE(TAG, "UART read error");
						break;
					}
				}
				else
				{
					ESP_LOGE(TAG, "No FD has been set in select()");
					break;
				}

实现效果如下:
在这里插入图片描述

4、PWM(LEDC)

关于ledc,官方描述如下:
在这里插入图片描述
对于流水灯,还有专门的函数
在这里插入图片描述
具体实现上如下所示,是一个渐变的过程,给一个频率和目标时间
在这里插入图片描述
完整代码如下

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include "esp_err.h"

#define LEDC_HS_TIMER LEDC_TIMER_0
#define LEDC_HS_MODE LEDC_HIGH_SPEED_MODE
#define LEDC_HS_CH0_GPIO (2)
#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0

#define LEDC_TEST_CH_NUM (1)
#define LEDC_TEST_DUTY (4000)	   // 渐变的变大最终目标占空比
#define LEDC_TEST_FADE_TIME (3000) // 变化时长

void app_main(void)
{
	int ch;

	/*
	 * Prepare and set configuration of timers
	 * that will be used by LED Controller
	 */
	ledc_timer_config_t ledc_timer = {
		.duty_resolution = LEDC_TIMER_13_BIT, // PWM占空比分辨率
		.freq_hz = 5000,					  // PWM信号频率
		.speed_mode = LEDC_HS_MODE,			  // 定时器模式
		.timer_num = LEDC_HS_TIMER,			  // 定时器序号
		.clk_cfg = LEDC_AUTO_CLK,			  // Auto select the source clock
	};
	// Set configuration of timer0 for high speed channels
	ledc_timer_config(&ledc_timer);

	/*
	 * Prepare individual configuration
	 * for each channel of LED Controller
	 * by selecting:
	 * - controller's channel number
	 * - output duty cycle, set initially to 0
	 * - GPIO number where LED is connected to
	 * - speed mode, either high or low
	 * - timer servicing selected channel
	 *   Note: if different channels use one timer,
	 *         then frequency and bit_num of these channels
	 *         will be the same
	 */
	// 配置定时器0的高速通道
	ledc_channel_config_t ledc_channel[LEDC_TEST_CH_NUM] = {
		{.channel = LEDC_HS_CH0_CHANNEL,
		 .duty = 0,
		 .gpio_num = LEDC_HS_CH0_GPIO,
		 .speed_mode = LEDC_HS_MODE,
		 .hpoint = 0,
		 .timer_sel = LEDC_HS_TIMER}};

	// 配置LED控制器
	for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++)
	{
		ledc_channel_config(&ledc_channel[ch]);
	}

	// 初始化淡入淡出服务
	ledc_fade_func_install(0); // 注册LEDC服务,在调用前使用,参数是作为是否允许中断

	while (1)
	{
		printf("1. LEDC fade up to duty = %d\n", LEDC_TEST_DUTY);
		for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++)
		{
			// 配置LEDC定时器
			ledc_set_fade_with_time(ledc_channel[ch].speed_mode,
									ledc_channel[ch].channel, LEDC_TEST_DUTY, LEDC_TEST_FADE_TIME);
			// 开始渐变
			ledc_fade_start(ledc_channel[ch].speed_mode,
							ledc_channel[ch].channel, LEDC_FADE_NO_WAIT);
		}
		// 等待渐变完成
		vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);

		printf("2. LEDC fade down to duty = 0\n");
		for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++)
		{
			ledc_set_fade_with_time(ledc_channel[ch].speed_mode,
									ledc_channel[ch].channel, 0, LEDC_TEST_FADE_TIME);
			ledc_fade_start(ledc_channel[ch].speed_mode,
							ledc_channel[ch].channel, LEDC_FADE_NO_WAIT);
		}
		vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);

		vTaskDelay(1000 / portTICK_PERIOD_MS);
	}
}

烧录后可以看到led灯呈现流水灯的变化

下面尝试输出pwm
示例代码:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include "esp_err.h"

#define LED_RED_IO 15 // 对应红灯的LED

#define LEDC_MAX_DUTY (8191)		   // 2的13次方-1(13位PWM)
#define PWM_RED_CHANNEL LEDC_CHANNEL_0 // 定义红灯通道

void app_main(void)
{
	ledc_channel_config_t g_ledc_red;
	// 定时器配置结构体
	ledc_timer_config_t ledc_timer;
	// 定时器配置->timer0
	ledc_timer.duty_resolution = LEDC_TIMER_13_BIT; // PWM分辨率
	ledc_timer.freq_hz = 5000;						// 频率
	ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE;	// 速度
	ledc_timer.timer_num = LEDC_TIMER_0;			// 选择定时器
	ledc_timer.clk_cfg = LEDC_USE_APB_CLK;
	ledc_timer_config(&ledc_timer); // 设置定时器PWM模式

	// PWM通道0配置->红色灯
	g_ledc_red.channel = PWM_RED_CHANNEL;		 // PWM通道
	g_ledc_red.duty = LEDC_MAX_DUTY;			 // 占空比
	g_ledc_red.gpio_num = LED_RED_IO;			 // IO映射
	g_ledc_red.speed_mode = LEDC_LOW_SPEED_MODE; // 速度
	g_ledc_red.timer_sel = LEDC_TIMER_0;		 // 选择定时器
	ledc_channel_config(&g_ledc_red);			 // 配置PWM

	ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_RED_CHANNEL, 4096);
	ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_RED_CHANNEL);

	while (1)
	{
	}
}

关注这几个参数
在这里插入图片描述

效果如下:
在这里插入图片描述

5、ADC

这个比较简单,直接贴代码

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_err.h"

// ADC所接的通道
#define ADC1_TEST_CHANNEL ADC1_CHANNEL_6

void app_main(void)
{
    // 12位分辨率
    adc1_config_width(ADC_WIDTH_BIT_12);
    //设置通道6和1.1V参考电压
    adc1_config_channel_atten(ADC1_TEST_CHANNEL, ADC_ATTEN_DB_0);

	while (1)
	{
		int val = adc1_get_raw(ADC1_TEST_CHANNEL);
		printf("adc value %d\r\n", val);

		vTaskDelay(1000 / portTICK_PERIOD_MS);
	}
}

从原理图上可以知道是这个参数
在这里插入图片描述

读取数值如下
在这里插入图片描述

6、DAC

有两个八位的dac,直接输出代码如下:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_err.h"
#include "driver/dac.h"

void app_main(void)
{
    dac_output_voltage(DAC_CHANNEL_1, 155);
    dac_output_enable(DAC_CHANNEL_1);
}

还可以输出余弦波

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_err.h"
#include "driver/dac.h"

void app_main(void)
{
    dac_cw_config_t config;
    config.en_ch = DAC_CHANNEL_1;
    config.freq = 55000;
    config.scale = DAC_CW_SCALE_1;
    config.phase = DAC_CW_PHASE_0;

	dac_cw_generator_config(&config);
	dac_cw_generator_enable();

    dac_output_enable(DAC_CHANNEL_1);
}


硬件上对应这两个接口
在这里插入图片描述

6、I2C

关于i2c的描述,官方手册上是这么描述的:
在这里插入图片描述
本文不关注iic实现的细节,因此直接贴代码,下面是驱动oled的示例

#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"

static const char *TAG = "i2c-simple-example";

#define I2C_MASTER_SCL_IO 19		/*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO 18		/*!< GPIO number used for I2C master data  */
#define I2C_MASTER_NUM 0			/*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ 100000	/*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 100

#define OLED_ADDR 0x3C // OLED的IIC地址,逻辑分析仪读出的

#define OLED_CMD 0
#define OLED_DATA 1

// OLED 6*8字库
unsigned char F6x8[][6] = {
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // sp
	{0x00, 0x00, 0x00, 0x2f, 0x00, 0x00}, // !
	{0x00, 0x00, 0x07, 0x00, 0x07, 0x00}, // "
	{0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14}, // #
	{0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12}, // $
	{0x00, 0x62, 0x64, 0x08, 0x13, 0x23}, // %
	{0x00, 0x36, 0x49, 0x55, 0x22, 0x50}, // &
	{0x00, 0x00, 0x05, 0x03, 0x00, 0x00}, // '
	{0x00, 0x00, 0x1c, 0x22, 0x41, 0x00}, // (
	{0x00, 0x00, 0x41, 0x22, 0x1c, 0x00}, // )
	{0x00, 0x14, 0x08, 0x3E, 0x08, 0x14}, // *
	{0x00, 0x08, 0x08, 0x3E, 0x08, 0x08}, // +
	{0x00, 0x00, 0x00, 0xA0, 0x60, 0x00}, // ,
	{0x00, 0x08, 0x08, 0x08, 0x08, 0x08}, // -
	{0x00, 0x00, 0x60, 0x60, 0x00, 0x00}, // .
	{0x00, 0x20, 0x10, 0x08, 0x04, 0x02}, // /
	{0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0
	{0x00, 0x00, 0x42, 0x7F, 0x40, 0x00}, // 1
	{0x00, 0x42, 0x61, 0x51, 0x49, 0x46}, // 2
	{0x00, 0x21, 0x41, 0x45, 0x4B, 0x31}, // 3
	{0x00, 0x18, 0x14, 0x12, 0x7F, 0x10}, // 4
	{0x00, 0x27, 0x45, 0x45, 0x45, 0x39}, // 5
	{0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6
	{0x00, 0x01, 0x71, 0x09, 0x05, 0x03}, // 7
	{0x00, 0x36, 0x49, 0x49, 0x49, 0x36}, // 8
	{0x00, 0x06, 0x49, 0x49, 0x29, 0x1E}, // 9
	{0x00, 0x00, 0x36, 0x36, 0x00, 0x00}, // :
	{0x00, 0x00, 0x56, 0x36, 0x00, 0x00}, // ;
	{0x00, 0x08, 0x14, 0x22, 0x41, 0x00}, // <
	{0x00, 0x14, 0x14, 0x14, 0x14, 0x14}, // =
	{0x00, 0x00, 0x41, 0x22, 0x14, 0x08}, // >
	{0x00, 0x02, 0x01, 0x51, 0x09, 0x06}, // ?
	{0x00, 0x32, 0x49, 0x59, 0x51, 0x3E}, // @
	{0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C}, // A
	{0x00, 0x7F, 0x49, 0x49, 0x49, 0x36}, // B
	{0x00, 0x3E, 0x41, 0x41, 0x41, 0x22}, // C
	{0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C}, // D
	{0x00, 0x7F, 0x49, 0x49, 0x49, 0x41}, // E
	{0x00, 0x7F, 0x09, 0x09, 0x09, 0x01}, // F
	{0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A}, // G
	{0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F}, // H
	{0x00, 0x00, 0x41, 0x7F, 0x41, 0x00}, // I
	{0x00, 0x20, 0x40, 0x41, 0x3F, 0x01}, // J
	{0x00, 0x7F, 0x08, 0x14, 0x22, 0x41}, // K
	{0x00, 0x7F, 0x40, 0x40, 0x40, 0x40}, // L
	{0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F}, // M
	{0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F}, // N
	{0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E}, // O
	{0x00, 0x7F, 0x09, 0x09, 0x09, 0x06}, // P
	{0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q
	{0x00, 0x7F, 0x09, 0x19, 0x29, 0x46}, // R
	{0x00, 0x46, 0x49, 0x49, 0x49, 0x31}, // S
	{0x00, 0x01, 0x01, 0x7F, 0x01, 0x01}, // T
	{0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F}, // U
	{0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F}, // V
	{0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F}, // W
	{0x00, 0x63, 0x14, 0x08, 0x14, 0x63}, // X
	{0x00, 0x07, 0x08, 0x70, 0x08, 0x07}, // Y
	{0x00, 0x61, 0x51, 0x49, 0x45, 0x43}, // Z
	{0x00, 0x00, 0x7F, 0x41, 0x41, 0x00}, // [
	{0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55}, // 55
	{0x00, 0x00, 0x41, 0x41, 0x7F, 0x00}, // ]
	{0x00, 0x04, 0x02, 0x01, 0x02, 0x04}, // ^
	{0x00, 0x40, 0x40, 0x40, 0x40, 0x40}, // _
	{0x00, 0x00, 0x01, 0x02, 0x04, 0x00}, // '
	{0x00, 0x20, 0x54, 0x54, 0x54, 0x78}, // a
	{0x00, 0x7F, 0x48, 0x44, 0x44, 0x38}, // b
	{0x00, 0x38, 0x44, 0x44, 0x44, 0x20}, // c
	{0x00, 0x38, 0x44, 0x44, 0x48, 0x7F}, // d
	{0x00, 0x38, 0x54, 0x54, 0x54, 0x18}, // e
	{0x00, 0x08, 0x7E, 0x09, 0x01, 0x02}, // f
	{0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C}, // g
	{0x00, 0x7F, 0x08, 0x04, 0x04, 0x78}, // h
	{0x00, 0x00, 0x44, 0x7D, 0x40, 0x00}, // i
	{0x00, 0x40, 0x80, 0x84, 0x7D, 0x00}, // j
	{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // k
	{0x00, 0x00, 0x41, 0x7F, 0x40, 0x00}, // l
	{0x00, 0x7C, 0x04, 0x18, 0x04, 0x78}, // m
	{0x00, 0x7C, 0x08, 0x04, 0x04, 0x78}, // n
	{0x00, 0x38, 0x44, 0x44, 0x44, 0x38}, // o
	{0x00, 0xFC, 0x24, 0x24, 0x24, 0x18}, // p
	{0x00, 0x18, 0x24, 0x24, 0x18, 0xFC}, // q
	{0x00, 0x7C, 0x08, 0x04, 0x04, 0x08}, // r
	{0x00, 0x48, 0x54, 0x54, 0x54, 0x20}, // s
	{0x00, 0x04, 0x3F, 0x44, 0x40, 0x20}, // t
	{0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C}, // u
	{0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C}, // v
	{0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C}, // w
	{0x00, 0x44, 0x28, 0x10, 0x28, 0x44}, // x
	{0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C}, // y
	{0x00, 0x44, 0x64, 0x54, 0x4C, 0x44}, // z
	{0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // horiz lines
};

// OLED 8*16字库
unsigned char F8X16[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0
	0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00, //! 1
	0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //" 2
	0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00, 0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00, // # 3
	0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00, 0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00, //$ 4
	0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00, //% 5
	0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10, //& 6
	0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //' 7
	0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00, //( 8
	0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00, //) 9
	0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00, 0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00, //* 10
	0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00, //+ 11
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, //, 12
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, //- 13
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, //. 14
	0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04, 0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00, /// 15
	0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00, // 0 16
	0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // 1 17
	0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00, // 2 18
	0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00, 0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // 3 19
	0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00, // 4 20
	0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00, 0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00, // 5 21
	0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // 6 22
	0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // 7 23
	0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00, // 8 24
	0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00, // 9 25
	0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, //: 26
	0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, //; 27
	0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, //< 28
	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, //= 29
	0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, //> 30
	0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00, //? 31
	0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00, 0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00, //@ 32
	0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20, // A 33
	0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // B 34
	0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // C 35
	0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // D 36
	0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00, // E 37
	0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, // F 38
	0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00, // G 39
	0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20, // H 40
	0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // I 41
	0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00, // J 42
	0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00, 0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00, // K 43
	0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, // L 44
	0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00, 0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00, // M 45
	0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00, // N 46
	0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // O 47
	0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00, // P 48
	0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00, // Q 49
	0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20, // R 50
	0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00, // S 51
	0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // T 52
	0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // U 53
	0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00, // V 54
	0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00, 0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00, // W 55
	0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08, 0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20, // X 56
	0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // Y 57
	0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00, // Z 58
	0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00, //[ 59
	0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00, //\ 60
	0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, //] 61
	0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //^ 62
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, //_ 63
	0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //` 64
	0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20, // a 65
	0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // b 66
	0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00, // c 67
	0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20, // d 68
	0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00, // e 69
	0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // f 70
	0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00, // g 71
	0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // h 72
	0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // i 73
	0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, // j 74
	0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00, // k 75
	0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // l 76
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F, // m 77
	0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // n 78
	0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // o 79
	0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00, // p 80
	0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80, // q 81
	0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00, // r 82
	0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00, // s 83
	0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00, // t 84
	0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20, // u 85
	0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00, // v 86
	0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, 0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00, // w 87
	0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00, // x 88
	0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00, // y 89
	0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00, // z 90
	0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40, //{ 91
	0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, //| 92
	0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, //} 93
	0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //~ 94
};

// OLED 中文字库
unsigned char Hzk[][32] = {
	{0xE0, 0x00, 0xFF, 0x10, 0xE4, 0x24, 0xFF, 0x24, 0xE4, 0x10, 0xE8, 0x27, 0xB4, 0x2C, 0xE0, 0x00},
	{0x01, 0x00, 0xFF, 0x10, 0x09, 0x05, 0xFF, 0x05, 0x19, 0x80, 0x4F, 0x20, 0x1F, 0x20, 0xCF, 0x00}, /*"懒",0*/

	{0x00, 0x08, 0x88, 0x88, 0x89, 0x8E, 0x88, 0xF8, 0x88, 0x8C, 0x8B, 0x88, 0x88, 0x08, 0x00, 0x00},
	{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xFF, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00}, /*"羊",1*/

	{0x00, 0x08, 0x88, 0x88, 0x89, 0x8E, 0x88, 0xF8, 0x88, 0x8C, 0x8B, 0x88, 0x88, 0x08, 0x00, 0x00},
	{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xFF, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00} /*"羊",2*/
};

// 函数声明
static esp_err_t i2c_master_init(void);
static esp_err_t OLED_WR_Byte(uint8_t data, uint8_t cmd_);
void OLED_Init(void);
void OLED_Set_Pos(uint8_t x, uint8_t y);
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size);
void OLED_Clear(void);
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size2);
uint32_t oled_pow(uint8_t m, uint8_t n);
void OLED_ShowString(uint8_t x, uint8_t y, char *chr, uint8_t Char_Size);
void OLED_ShowCHinese(uint8_t x, uint8_t y, uint8_t no);

/**
 * @description: 主函数
 * @return       无
 */
void app_main(void)
{
	// IIC总线主机初始化
	ESP_ERROR_CHECK(i2c_master_init());
	ESP_LOGI(TAG, "I2C initialized successfully");

	// OLED屏幕初始化
	OLED_Init();

	// 显示汉字
	OLED_ShowCHinese(0 * 18, 0, 0);
	OLED_ShowCHinese(1 * 18, 0, 1);
	OLED_ShowCHinese(2 * 18, 0, 2);

	// 显示单个字符
	OLED_ShowChar(0, 2, 'Q', 16);

	// 显示字符串
	OLED_ShowString(0, 4, "Fairy tale", 16);

	// 显示数字
	OLED_ShowNum(0, 6, 8266, 6, 16);

	// 删除IIC设备
	// ESP_ERROR_CHECK(i2c_driver_delete(I2C_MASTER_NUM));
	// ESP_LOGI(TAG, "I2C unitialized successfully");
}

/**
 * @brief i2c master initialization
 */
static esp_err_t i2c_master_init(void)
{
	int i2c_master_port = I2C_MASTER_NUM;

	i2c_config_t conf = {
		.mode = I2C_MODE_MASTER,
		.sda_io_num = I2C_MASTER_SDA_IO, // 18
		.scl_io_num = I2C_MASTER_SCL_IO, // 19
		.sda_pullup_en = GPIO_PULLUP_ENABLE,
		.scl_pullup_en = GPIO_PULLUP_ENABLE,
		.master.clk_speed = I2C_MASTER_FREQ_HZ,
	};

	i2c_param_config(i2c_master_port, &conf);

	return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

/**
 * @description: OLED 发送一个字节
 * @return       错误信息
 * @param {uint8_t} data 需要发送的内容,数据或者命令
 * @param {uint8_t} cmd_ 1:发送数据 0:发送命令
 */
static esp_err_t OLED_WR_Byte(uint8_t data, uint8_t cmd_)
{
	int ret;

	uint8_t write_buf[2] = {((cmd_ == 1) ? (0x40) : (0x00)), data};

	ret = i2c_master_write_to_device(I2C_MASTER_NUM, OLED_ADDR, write_buf, sizeof(write_buf), I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);

	return ret;
}

/**
 * @description: OLED 屏幕初始化
 * @return       无
 */
void OLED_Init(void)
{
	OLED_WR_Byte(0xAE, OLED_CMD); //--display off
	OLED_WR_Byte(0x00, OLED_CMD); //---set low column address
	OLED_WR_Byte(0x10, OLED_CMD); //---set high column address
	OLED_WR_Byte(0x40, OLED_CMD); //--set start line address
	OLED_WR_Byte(0xB0, OLED_CMD); //--set page address
	OLED_WR_Byte(0x81, OLED_CMD); // contract control
	OLED_WR_Byte(0xFF, OLED_CMD); //--128
	OLED_WR_Byte(0xA1, OLED_CMD); // set segment remap
	OLED_WR_Byte(0xA6, OLED_CMD); //--normal / reverse
	OLED_WR_Byte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3F, OLED_CMD); //--1/32 duty
	OLED_WR_Byte(0xC8, OLED_CMD); // Com scan direction
	OLED_WR_Byte(0xD3, OLED_CMD); //-set display offset
	OLED_WR_Byte(0x00, OLED_CMD); //
	OLED_WR_Byte(0xD5, OLED_CMD); // set osc division
	OLED_WR_Byte(0x80, OLED_CMD); //
	OLED_WR_Byte(0xD8, OLED_CMD); // set area color mode off
	OLED_WR_Byte(0x05, OLED_CMD); //
	OLED_WR_Byte(0xD9, OLED_CMD); // Set Pre-Charge Period
	OLED_WR_Byte(0xF1, OLED_CMD); //
	OLED_WR_Byte(0xDA, OLED_CMD); // set com pin configuartion
	OLED_WR_Byte(0x12, OLED_CMD); //
	OLED_WR_Byte(0xDB, OLED_CMD); // set Vcomh
	OLED_WR_Byte(0x30, OLED_CMD); //
	OLED_WR_Byte(0x8D, OLED_CMD); // set charge pump enable
	OLED_WR_Byte(0x14, OLED_CMD); //
	OLED_WR_Byte(0xAF, OLED_CMD); //--turn on oled panel
	OLED_Clear();
}

/**
 * @description: OLED 屏幕 设置坐标
 * @return       无
 * @param {uint8_t} x 坐标x轴,范围0~127
 * @param {uint8_t} y 坐标y轴,范围0~63
 */
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
	OLED_WR_Byte(0xb0 + y, OLED_CMD);
	OLED_WR_Byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD);
	OLED_WR_Byte((x & 0x0f), OLED_CMD);
}

/**
 * @description: OLED 清屏
 * @return       无
 */
void OLED_Clear(void)
{
	uint8_t i, n;
	for (i = 0; i < 8; i++)
	{
		OLED_WR_Byte(0xb0 + i, OLED_CMD);
		OLED_WR_Byte(0x00, OLED_CMD);
		OLED_WR_Byte(0x10, OLED_CMD);
		for (n = 0; n < 128; n++)
			OLED_WR_Byte(0, OLED_DATA);
	}
}

/**
 * @description: OLED 显示单个字符
 * @return       无
 * @param {uint8_t} x 显示字符的x坐标,范围0~127
 * @param {uint8_t} y 显示字符的y坐标,字符大小为16,取值0,2,4,6;字符大小6,取值0,1,2,3,4,5,6,7
 * @param {uint8_t} chr 显示的单个字符,在字库中出现的字符
 * @param {uint8_t} Char_Size 字符大小,取16或者8
 */
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size)
{
	uint8_t c = 0;
	uint8_t i = 0;
	c = chr - ' ';
	if (x > 127)
	{
		x = 0;
		y = y + 2;
	}
	if (Char_Size == 16)
	{
		OLED_Set_Pos(x, y);
		for (i = 0; i < 8; i++)
			OLED_WR_Byte(F8X16[c * 16 + i], OLED_DATA);
		OLED_Set_Pos(x, y + 1);
		for (i = 0; i < 8; i++)
			OLED_WR_Byte(F8X16[c * 16 + i + 8], OLED_DATA);
	}
	else
	{
		OLED_Set_Pos(x, y);
		for (i = 0; i < 6; i++)
			OLED_WR_Byte(F6x8[c][i], OLED_DATA);
	}
}

/**
 * @description: OLED 显示字符串,会自动换行
 * @return       无
 * @param {uint8_t} x 显示字符串第一个字符的x坐标,范围0~127
 * @param {uint8_t} y 显示字符串第一个字符的y坐标,字符大小为16,取值0,2,4,6;字符大小6,取值0,1,2,3,4,5,6,7
 * @param {char} *chr 显示的字符串
 * @param {uint8_t} Char_Size 字符大小,取16或者8
 */
void OLED_ShowString(uint8_t x, uint8_t y, char *chr, uint8_t Char_Size)
{
	unsigned char j = 0;
	while (chr[j] != '\0')
	{
		OLED_ShowChar(x, y, chr[j], Char_Size);
		x += 8;
		if (x > 120)
		{
			x = 0;
			y += 2;
		}
		j++;
	}
}

/**
 * @description: OLED 显示汉字
 * @return       无
 * @param {uint8_t} x 显示汉字的x坐标
 * @param {uint8_t} y 显示汉字的y坐标
 * @param {uint8_t} no 显示汉字在字库中的序号
 */
void OLED_ShowCHinese(uint8_t x, uint8_t y, uint8_t no)
{
	uint8_t t, adder = 0;
	OLED_Set_Pos(x, y);
	for (t = 0; t < 16; t++)
	{
		OLED_WR_Byte(Hzk[2 * no][t], OLED_DATA);
		adder += 1;
	}
	OLED_Set_Pos(x, y + 1);
	for (t = 0; t < 16; t++)
	{
		OLED_WR_Byte(Hzk[2 * no + 1][t], OLED_DATA);
		adder += 1;
	}
}

/**
 * @description: 求m^n的函数
 * @return       m^n的结果
 * @param {uint8_t} m 底数
 * @param {uint8_t} n 指数
 */
uint32_t oled_pow(uint8_t m, uint8_t n)
{
	uint32_t result = 1;
	while (n--)
		result *= m;
	return result;
}

/**
 * @description: OLED 显示数字
 * @return       无
 * @param {uint8_t} x 显示数字的第一个位置的x坐标
 * @param {uint8_t} y 显示数字的第一个位置的y坐标
 * @param {uint32_t} num 欲显示的数字
 * @param {uint8_t} len 显示所占的长度,不建议小于真正要显示的数字的长度
 * @param {uint8_t} size2 显示的数字的大小,16、8可选
 */
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size2)
{
	uint8_t t, temp;
	uint8_t enshow = 0;
	for (t = 0; t < len; t++)
	{
		temp = (num / oled_pow(10, len - t - 1)) % 10;
		if (enshow == 0 && t < (len - 1))
		{
			if (temp == 0)
			{
				OLED_ShowChar(x + (size2 / 2) * t, y, ' ', size2);
				continue;
			}
			else
				enshow = 1;
		}
		OLED_ShowChar(x + (size2 / 2) * t, y, temp + '0', size2);
	}
}

7、SPI

官方手册上关于spi的描述如下:
在这里插入图片描述
这里需要注意就是:SPI0和SPI1在内部用于访问ESP32所连接的闪存。SPI2和SPI3是通用SPI控制器,分别称为HSPI和VSPI。它们向用户开放。

SPI的引脚同样可以使用GPIO交换矩阵进行连接,但是这会产生一定的延迟,因此如果想要高速的spi就不能用交换矩阵了,SPI2和SPI3的默认引脚如下:

Pin NameSPI2SPI3
CS155
SCLK1418
MISO1219
MOSI1323
QUADWP222
QUADHD421

同样,我们不关心spi的原理,这里尝试驱动一个st7735的lcd屏幕,屏幕链接如下:
https://item.taobao.com/item.htm?_u=p3qifgeve390&id=560176729178&spm=a1z09.2.0.0.65e92e8dHdgr8B

在这里插入图片描述

直接贴代码:
st7735.c


#include "ST7735.h"

/*
 The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct.
*/
typedef struct {
  uint8_t cmd;
  uint8_t data[16];
  uint8_t databytes;  // No of data in data, 0xFF = end of cmds
} lcd_init_cmd_t;

static spi_device_handle_t spi_dev;

#define ST7735_TFTWIDTH_128 128   // for 1.44 and mini
#define ST7735_TFTWIDTH_80 80     // for mini
#define ST7735_TFTHEIGHT_128 128  // for 1.44" display
#define ST7735_TFTHEIGHT_160 160  // for 1.8" and mini display

#define LCD_WIDTH 128
#define LCD_HEIGHT 160

// ST77XX commands
#define ST_CMD_DELAY 0x80  // special signifier for command lists

#define ST77XX_NOP 0x00
#define ST77XX_SWRESET 0x01
#define ST77XX_RDDID 0x04
#define ST77XX_RDDST 0x09

#define ST77XX_SLPIN 0x10
#define ST77XX_SLPOUT 0x11
#define ST77XX_PTLON 0x12
#define ST77XX_NORON 0x13

#define ST77XX_INVOFF 0x20
#define ST77XX_INVON 0x21
#define ST77XX_DISPOFF 0x28
#define ST77XX_DISPON 0x29
#define ST77XX_CASET 0x2A
#define ST77XX_RASET 0x2B
#define ST77XX_RAMWR 0x2C
#define ST77XX_RAMRD 0x2E

#define ST77XX_PTLAR 0x30
#define ST77XX_TEOFF 0x34
#define ST77XX_TEON 0x35
#define ST77XX_MADCTL 0x36
#define ST77XX_COLMOD 0x3A

#define ST77XX_MADCTL_MY 0x80
#define ST77XX_MADCTL_MX 0x40
#define ST77XX_MADCTL_MV 0x20
#define ST77XX_MADCTL_ML 0x10
#define ST77XX_MADCTL_RGB 0x00

#define ST77XX_RDID1 0xDA
#define ST77XX_RDID2 0xDB
#define ST77XX_RDID3 0xDC
#define ST77XX_RDID4 0xDD

// ST7735 commands
#define ST7735_MADCTL_BGR 0x08
#define ST7735_MADCTL_MH 0x04

#define ST7735_FRMCTR1 0xB1
#define ST7735_FRMCTR2 0xB2
#define ST7735_FRMCTR3 0xB3
#define ST7735_INVCTR 0xB4
#define ST7735_DISSET5 0xB6

#define ST7735_PWCTR1 0xC0
#define ST7735_PWCTR2 0xC1
#define ST7735_PWCTR3 0xC2
#define ST7735_PWCTR4 0xC3
#define ST7735_PWCTR5 0xC4
#define ST7735_VMCTR1 0xC5

#define ST7735_PWCTR6 0xFC

#define ST7735_GMCTRP1 0xE0
#define ST7735_GMCTRN1 0xE1

// RGB-565 16bit, 128*160;
static uint8_t display_buff[LCD_WIDTH * LCD_HEIGHT * 2];

DRAM_ATTR static const lcd_init_cmd_t st7735_init_cmds[] = {
    // software reset with delay
    {ST77XX_SWRESET, {0}, ST_CMD_DELAY},
    // Out of sleep mode with delay
    {ST77XX_SLPOUT, {0}, ST_CMD_DELAY},
    // Framerate ctrl - normal mode. Rate = fosc/(1x2+40) * (LINE+2C+2D)
    {ST7735_FRMCTR1, {0x01, 0x2C, 0x2D}, 3},
    // Framerate ctrl - idle mode.  Rate = fosc/(1x2+40) * (LINE+2C+2D)
    {ST7735_FRMCTR2, {0x01, 0x2C, 0x2D}, 3},
    // Framerate - partial mode. Dot/Line inversion mode
    {ST7735_FRMCTR3, {0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D}, 6},
    // Display inversion ctrl: No inversion
    {ST7735_INVCTR, {0x07}, 1},
    // Power control1 set GVDD: -4.6V, AUTO mode.
    {ST7735_PWCTR1, {0xA2, 0x02, 0x84}, 3},
    // Power control2 set VGH/VGL: VGH25=2.4C VGSEL=-10 VGH=3 * AVDD
    {ST7735_PWCTR2, {0xC5}, 1},
    // Power control3 normal mode(Full color): Op-amp current small, booster voltage
    {ST7735_PWCTR3, {0x0A, 0x00}, 2},
    // Power control4 idle mode(8-colors): Op-amp current small & medium low
    {ST7735_PWCTR4, {0x8A, 0x2A}, 2},
    // Power control5 partial mode + full colors
    {ST7735_PWCTR5, {0x8A, 0xEE}, 2},
    // VCOMH VoltageVCOM control 1: VCOMH=0x0E=2.850
    {ST7735_VMCTR1, {0x0E}, 1},
    // Display Inversion Off
    {ST77XX_INVOFF, {0}, 0},
    // Memory Data Access Control: top-bottom/left-right refresh
    {ST77XX_MADCTL, {0xC8}, 1},
    // Color mode, Interface Pixel Format: RGB-565, 16-bit/pixel
    {ST77XX_COLMOD, {0x05}, 1},

    // Column Address Set: 2, 127+2
    {ST77XX_CASET, {0x00, 0x02, 0x00, 0x7F + 0x02}, 4},
    // Row Address Set: 1,159+1
    {ST77XX_RASET, {0x00, 0x01, 0x00, 0x9F + 0x01}, 4},

    // Gamma Adjustments (pos. polarity). Not entirely necessary, but provides accurate colors.
    {ST7735_GMCTRP1,
     {0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10},
     16},
    // Gamma Adjustments (neg. polarity). Not entirely necessary, but provides accurate colors.
    {ST7735_GMCTRN1,
     {0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10},
     16},
    // Normal Display Mode On
    {ST77XX_NORON, {0}, ST_CMD_DELAY},
    // Display On
    {ST77XX_DISPON, {0}, ST_CMD_DELAY},
    {0, {0}, 0xFF},
};

/* Send a command to the LCD. Uses spi_device_polling_transmit, which waits
 * until the transfer is complete.
 *
 * Since command transactions are usually small, they are handled in polling
 * mode for higher speed. The overhead of interrupt transactions is more than
 * just waiting for the transaction to complete.
 */
static void st7735_cmd(const uint8_t cmd) {
  esp_err_t ret;
  spi_transaction_t t;
  memset(&t, 0, sizeof(t));                        // Zero out the transaction
  t.length = 8;                                    // Command is 8 bits
  t.tx_buffer = &cmd;                              // The data is the cmd itself
  t.user = (void *)0;                              // D/C needs to be set to 0
  ret = spi_device_polling_transmit(spi_dev, &t);  // Transmit!
  assert(ret == ESP_OK);                           // Should have had no issues.
}

/* Send data to the LCD. Uses spi_device_polling_transmit, which waits until the
 * transfer is complete.
 *
 * Since data transactions are usually small, they are handled in polling
 * mode for higher speed. The overhead of interrupt transactions is more than
 * just waiting for the transaction to complete.
 */
static void st7735_data(const uint8_t *data, int len) {
  esp_err_t ret;
  spi_transaction_t t;
  if (len == 0) return;                            // no need to send anything
  memset(&t, 0, sizeof(t));                        // Zero out the transaction
  t.length = len * 8;                              // Len is in bytes, transaction length is in bits.
  t.tx_buffer = data;                              // Data
  t.user = (void *)1;                              // D/C needs to be set to 1
  ret = spi_device_polling_transmit(spi_dev, &t);  // Transmit!
  assert(ret == ESP_OK);                           // Should have had no issues.
}

// This function is called (in irq context!) just before a transmission starts. It will
// set the D/C line to the value indicated in the user field.
static void lcd_spi_pre_transfer_callback(spi_transaction_t *t) {
  int dc = (int)t->user;
  gpio_set_level(PIN_NUM_DC, dc);
}

static void st7735_set_address_window(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) {
  uint8_t data[4];
  st7735_cmd(ST77XX_CASET);
  data[0] = 0x00;
  data[1] = x0 + 0x02;
  data[2] = 0x00;
  data[3] = x1 + 0x02;
  st7735_data(data, 4);

  st7735_cmd(ST77XX_RASET);
  data[0] = 0x00;
  data[1] = y0 + 0x01;
  data[2] = 0x00;
  data[3] = y1 + 0x01;
  st7735_data(data, 4);

  // memory write
  st7735_cmd(ST77XX_RAMWR);
}

void st7735_fill_screen(uint16_t color) {
  for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT * 2); i = i + 2) {
    display_buff[i] = color & 0xFF;
    display_buff[i + 1] = color >> 8;
  }

  st7735_set_address_window(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);
  st7735_data(display_buff, LCD_WIDTH * LCD_HEIGHT * 2);
}

void st7735_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
  // rudimentary clipping (drawChar w/big text requires this)
  if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)) return;
  if ((x + w - 1) >= LCD_WIDTH) w = LCD_WIDTH - x;
  if ((y + h - 1) >= LCD_HEIGHT) h = LCD_HEIGHT - y;

  st7735_set_address_window(x, y, x + w - 1, y + h - 1);

  st7735_cmd(ST77XX_RAMWR);

  for (int i = 0; i < (w * h * 2); i++) {
    display_buff[i] = color & 0xFF;
    display_buff[i + 1] = color >> 8;
  }
  st7735_data(display_buff, w * h * 2);
}

void st7735_invert_color(int i) {
  if (i) {
    st7735_cmd(ST77XX_INVON);
  } else {
    st7735_cmd(ST77XX_INVOFF);
  }
}

void st7735_init() {
  esp_err_t ret;
  // esp32 spi configuration
  spi_bus_config_t buscfg = {.miso_io_num = PIN_NUM_MISO,
                             .mosi_io_num = PIN_NUM_MOSI,
                             .sclk_io_num = PIN_NUM_CLK,
                             .quadwp_io_num = -1,  // unused
                             .quadhd_io_num = -1,  // unused
                             .max_transfer_sz = LCD_WIDTH * LCD_HEIGHT * 2};

  spi_device_interface_config_t devcfg = {
      .clock_speed_hz = 10 * 1000 * 1000,       // Clock out at 10 MHz
      .mode = 0,                                // SPI mode 0
      .spics_io_num = PIN_NUM_CS,               // CS pin
      .queue_size = 7,                          // We want to be able to queue 7 transactions at a time
      .pre_cb = lcd_spi_pre_transfer_callback,  // Specify pre-transfer callback to handle D/C line
  };

  spi_host_device_t spi_host = VSPI_HOST;
#ifdef CONFIG_ST7735_HOST_HSPI
  spi_host = HSPI_HOST;
#endif

  // Initialize the SPI bus
  ret = spi_bus_initialize(spi_host, &buscfg, 1);
  ESP_ERROR_CHECK(ret);
  // Attach the LCD to the SPI bus
  ret = spi_bus_add_device(spi_host, &devcfg, &spi_dev);
  ESP_ERROR_CHECK(ret);

  // Initialize non-SPI GPIOs
  gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT);
  gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT);
  gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT);

  // Reset the display
  gpio_set_level(PIN_NUM_RST, 0);
  vTaskDelay(500 / portTICK_PERIOD_MS);
  gpio_set_level(PIN_NUM_RST, 1);
  vTaskDelay(500 / portTICK_PERIOD_MS);

  // Send all the init commands
  int cmd = 0;
  while (st7735_init_cmds[cmd].databytes != 0xff) {
    st7735_cmd(st7735_init_cmds[cmd].cmd);
    st7735_data(st7735_init_cmds[cmd].data, st7735_init_cmds[cmd].databytes & 0x1F);
    if (st7735_init_cmds[cmd].databytes & ST_CMD_DELAY) {
      vTaskDelay(100 / portTICK_PERIOD_MS);
    }
    cmd++;
  }

  /// Enable backlight
  gpio_set_level(PIN_NUM_BCKL, 1);
}

void st7735_draw_pixel(int16_t x, int16_t y, uint16_t color) {
  if ((x < 0) || (x >= LCD_WIDTH) || (y < 0) || (y >= LCD_HEIGHT)) return;

  display_buff[0] = color & 0xFF;
  display_buff[1] = color >> 8;
  st7735_set_address_window(x, y, 1, 1);
  st7735_data(display_buff, 2);
}

void st7735_draw_char(int16_t x, int16_t y, char c, int16_t color, int16_t bg_color, uint8_t size) {
  if ((x >= LCD_WIDTH) ||          // Clip right
      (y >= LCD_HEIGHT) ||         // Clip bottom
      ((x + 6 * size - 1) < 0) ||  // Clip left
      ((y + 8 * size - 1) < 0))    // Clip top
    return;

  for (int8_t i = 0; i < 5; i++) {  // Char bitmap = 5 columns
    uint8_t line = std_font[c * 5 + i];
    for (int8_t j = 0; j < 8; j++, line >>= 1) {
      if (line & 1) {
        if (size == 1) {
          st7735_draw_pixel(x + i, y + j, color);
        } else {
          st7735_rect(x + (i * size), y + (j * size), size, size, color);
        }
      } else if (bg_color != color) {
        if (size == 1) {
          st7735_draw_pixel(x + i, y + j, bg_color);
        } else {
          st7735_rect(x + (i * size), y + (j * size), size, size, bg_color);
        }
      }
    }
  }
}

uint32_t st7735_draw_string(uint16_t x, uint16_t y, const char *pt, int16_t color, int16_t bg_color, uint8_t size) {
  // check row and colume
  uint32_t x_offset = 5 + 1, y_offset = 7;  // font size 5x7.

  uint32_t count = 0;
  if (y > 15) return 0;
  while (*pt) {
    st7735_draw_char(x * x_offset, y * y_offset, *pt, color, bg_color, size);
    pt++;
    x = x + size;
    if (x > 20) return count;  // number of characters printed
    count++;
  }
  return count;  // number of characters printed
}

st7735.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "ascii_font.h"

#define PIN_NUM_MISO -1
#define PIN_NUM_MOSI 23 // SDA
#define PIN_NUM_CLK 18
#define PIN_NUM_CS 5
#define PIN_NUM_DC 21
#define PIN_NUM_RST 22

// LCD backlight contorl
#define PIN_NUM_BCKL 19
#define CONFIG_USE_COLOR_RBG565

#ifdef CONFIG_USE_COLOR_RBG565 // R-B-G 5-6-5
// Some ready-made 16-bit (RBG-565) color settings:
#define COLOR_BLACK 0x0000
#define COLOR_WHITE 0xFFFF
#define COLOR_RED 0xF800
#define COLOR_GREEN 0x001F
#define COLOR_BLUE 0x07E0
#define COLOR_CYAN 0x07FF
#define COLOR_MAGENTA 0xFFE0
#define COLOR_YELLOW 0xF81F
#define COLOR_GRAY 0x8410
#define COLOR_OLIVE 0x8011
#else // R-G-B 5-6-5
// Some ready-made 16-bit (RGB-565) color settings:
#define COLOR_BLACK 0x0000
#define COLOR_WHITE 0xFFFF
#define COLOR_RED 0xF800
#define COLOR_GREEN 0x07E0
#define COLOR_BLUE 0x001F
#define COLOR_CYAN 0x07FF
#define COLOR_MAGENTA 0xF81F
#define COLOR_YELLOW 0xFFE0
#define COLOR_GRAY 0x8410
#define COLOR_OLIVE 0x8400
#endif

void st7735_init();
void st7735_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void st7735_invert_color(int i);
void st7735_fill_screen(uint16_t color);
uint32_t st7735_draw_string(uint16_t x, uint16_t y, const char *pt, int16_t color, int16_t bg_color, uint8_t size);
void st7735_draw_char(int16_t x, int16_t y, char c, int16_t color, int16_t bg_color, uint8_t size);

在主函数上做一下简单调用即可
在这里插入图片描述

8、FLASH(NVS)

就是flash操作,直接贴代码

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"

void app_main(void)
{
	// Initialize NVS
	esp_err_t err = nvs_flash_init();
	if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
	{
		// NVS partition was truncated and needs to be erased
		// Retry nvs_flash_init
		ESP_ERROR_CHECK(nvs_flash_erase());
		err = nvs_flash_init();
	}
	ESP_ERROR_CHECK(err);

	// Open
	printf("\n");
	printf("Opening Non-Volatile Storage (NVS) handle... ");
	nvs_handle_t my_handle;
	err = nvs_open("storage", NVS_READWRITE, &my_handle);
	if (err != ESP_OK)
	{
		printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
	}
	else
	{
		printf("Done\n");

		// Read
		printf("Reading restart counter from NVS ... ");
		int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
		err = nvs_get_i32(my_handle, "restart_counter", &restart_counter);
		switch (err)
		{
		case ESP_OK:
			printf("Done\n");
			printf("Restart counter = %ld\n", restart_counter);
			break;
		case ESP_ERR_NVS_NOT_FOUND:
			printf("The value is not initialized yet!\n");
			break;
		default:
			printf("Error (%s) reading!\n", esp_err_to_name(err));
		}

		// Write
		printf("Updating restart counter in NVS ... ");
		restart_counter++;
		err = nvs_set_i32(my_handle, "restart_counter", restart_counter);
		printf((err != ESP_OK) ? "Failed!\n" : "Done\n");

		// Commit written value.
		// After setting any values, nvs_commit() must be called to ensure changes are written
		// to flash storage. Implementations may write to storage at other times,
		// but this is not guaranteed.
		printf("Committing updates in NVS ... ");
		err = nvs_commit(my_handle);
		printf((err != ESP_OK) ? "Failed!\n" : "Done\n");

		// Close
		nvs_close(my_handle);
	}

	printf("\n");

	// Restart module
	for (int i = 10; i >= 0; i--)
	{
		printf("Restarting in %d seconds...\n", i);
		vTaskDelay(1000 / portTICK_PERIOD_MS);
	}
	printf("Restarting now.\n");
	fflush(stdout);
	esp_restart();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桃成蹊2.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值