freertos入门—创建第一个多任务程序
本实验的目的是创建两个任务,一个任务控制LED灯闪烁,另一个灯控制OLED屏幕的显示。
1 STM32CubeMx配置
双击运行STM32CubeMX,在首页选择“ACCESS TO MCU SELECTOR”,如下图所示:
在MCU选型界面,输入自己想要开发的芯片型号,如:STM32F103C8T6:
2 配置时钟
在“System Core”的“RCC”处选择外部高速时钟源和低速时钟源,这里我们选择外部高速时钟源,如下图所示:
此外,本实验使用FreeRTOS,FreeRTOS的时基使用的是Systick,而STM32CubeMX中默认的hal库时基也是Systick,为了避免可能的冲突,最好将HAL库的时基换为其它的硬件定时器。如下图所示:
最后在时钟配置界面配置系统时钟频率,STM32F103的最高频率为72Mhz,所以直接输入MCU允许的最高时钟频率然后按回车:
回车后,STM32CubeMX会自动计算得到各个分频系数与倍频系数:
点击完“OK”后,STM32CubeMX开始自动配置时钟,配置成功后,结果如下图所示:
3 配置GPIO与IIC
3.1 配置GPIO
在本实验中,我们需要使用LED,LED所使用的GPIO引脚为PC13,如下图所示:
在STM32CubeMX的引脚配置界面,选择PC13:
在芯片图中,点击PC13,会弹出此IO支持的模式:
选择GPIO Output,使PC13配置为通用输出IO,以此驱动LED的亮灭。
3.2 配置IIC
在本实验中,OLED屏幕的显示依靠IIC通信,因此我们需要配置单片机的IIC引脚。在Connectivity中选择I2C1并使能:
GPIO引脚与参数设置保持默认即可,如下图所示:
4 配置FreeRTOS
STM32CubeMX已经将FreeRTOS集成到工具中,并且将RTOS的接口进行了封装CMSIS-RTOS V1/V2,相较之于 V1 版本的 CMSIS-RTOS API,V2 版本的 API 的兼容性更高,为了将来的开发和移植,我们使用 V2 版本的 API:
选择了CMSIS V2接口后,还需要进一步配置FreeRTOS 的参数和功能。
4.1 配置参数
FreeRTSOS的参数包括时基频率、任务堆栈大小、是否使能互斥锁等,需要我们根据对FreeRTOS的了解以及项目开发的需求来定制参数。
4.2 添加任务
使用STM32CubeMX,可以手动添加任务、队列、信号量、互斥锁、定时器等。不过我们并不想太依赖STM32CubeMX工具,而是选择用代码使用这些对象。
5 生成Keil MDK的工程
当对外设配置完成后,我们在“Project Manager”中设置工程的名称、存储路径以及开发的IDE:
接着去界面“Code Generator”设置、生成工程:
生成代码后,会弹出以下提示,点击Yes下载所依赖的文件:
接着,会弹出以下提醒,我们点击“Open Folder”可以打开工程目录:
6 添加用户代码
6.1 添加LED驱动程序
在代码中添加 driver_led.c 与 driver_led.h 文件:
#include "driver_led.h"
#include "driver_timer.h"
#include "gpio.h"
/**********************************************************************
* 函数名称: Led_Init
* 功能描述: LED初始化函数, 在HAL的初始化代码里已经配置好了GPIO引脚,
* 所以本函数可以写为空
* 但是为了不依赖于stm32cubemx, 此函数也实现了GPIO的配置
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 0 - 成功, 其他值 - 失败
***********************************************************************/
int Led_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
return 0;
}
/**********************************************************************
* 函数名称: Led_Control
* 功能描述: LED控制函数
* 输入参数: which-哪个LED, 在driver_led.h里定义, 比如LED_GREEN
* on-状态, 1-亮, 0-灭
* 输出参数: 无
* 返 回 值: 0 - 成功, 其他值 - 失败
***********************************************************************/
int Led_Control(int on)
{
if (on)
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
return 0;
}
/**********************************************************************
* 函数名称: Led_Test
* 功能描述: Led测试程序
* 输入参数: 无
* 输出参数: 无
* 无
* 返 回 值: 0 - 成功, 其他值 - 失败
***********************************************************************/
void Led_Test(void)
{
Led_Init();
while (1)
{
Led_Control(LED_ON);
mdelay(500);
Led_Control(LED_OFF);
mdelay(500);
}
}
#ifndef _DRIVER_LED_H
#define _DRIVER_LED_H
#define LED_ON 1
#define LED_OFF 0
int Led_Init(void);
int Led_Control(int on);
void Led_Test(void);
#endif
若是只添加 driver_led.c 和 driver_led.h 文件,编译代码会报错,原因是我们没有添加延时函数,我们需要添加 driver_timer.c 与 driver_timer.h 文件:
#include "driver_timer.h"
#include "stm32f1xx_hal.h"
/**********************************************************************
* 函数名称: udelay
* 功能描述: us级别的延时函数(复制rt-thread的代码)
* 输入参数: us - 延时多少us
* 输出参数: 无
* 返 回 值: 无
***********************************************************************/
void udelay(int us)
{
#if 0
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD;
ticks = us * reload / (1000); /* 假设reload对应1ms */
told = SysTick->VAL;
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
{
tcnt += told - tnow;
}
else
{
tcnt += reload - tnow + told;
}
told = tnow;
if (tcnt >= ticks)
{
break;
}
}
}
#else
extern TIM_HandleTypeDef htim4;
TIM_HandleTypeDef *hHalTim = &htim4;
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = __HAL_TIM_GET_AUTORELOAD(hHalTim);
ticks = us * reload / (1000); /* 假设reload对应1ms */
told = __HAL_TIM_GET_COUNTER(hHalTim);
while (1)
{
tnow = __HAL_TIM_GET_COUNTER(hHalTim);
if (tnow != told)
{
if (tnow > told)
{
tcnt += tnow - told;
}
else
{
tcnt += reload - told + tnow;
}
told = tnow;
if (tcnt >= ticks)
{
break;
}
}
}
#endif
}
/**********************************************************************
* 函数名称: mdelay
* 功能描述: ms级别的延时函数
* 输入参数: ms - 延时多少ms
* 输出参数: 无
* 返 回 值: 无
***********************************************************************/
void mdelay(int ms)
{
for (int i = 0; i < ms; i++)
udelay(1000);
}
/**********************************************************************
* 函数名称: system_get_ns
* 功能描述: 获得系统时间(单位ns)
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 系统时间(单位ns)
***********************************************************************/
uint64_t system_get_ns(void)
{
//extern uint32_t HAL_GetTick(void);
extern TIM_HandleTypeDef htim4;
TIM_HandleTypeDef *hHalTim = &htim4;
uint64_t ns = HAL_GetTick();
uint64_t cnt;
uint64_t reload;
cnt = __HAL_TIM_GET_COUNTER(hHalTim);
reload = __HAL_TIM_GET_AUTORELOAD(hHalTim);
ns *= 1000000;
ns += cnt * 1000000 / reload;
return ns;
}
#ifndef _DRIVER_TIMER_H
#define _DRIVER_TIMER_H
#include <stdint.h>
void udelay(int us);
void mdelay(int ms);
uint64_t system_get_ns(void);
#endif
6.2 添加OLED驱动程序
在代码中添加 driver_oled.c 与 driver_oled.h 文件:
#include "driver_oled.h"
#include "ascii_font.c"
#include "stm32f1xx_hal.h"
/*
全屏点亮或者熄灭
*/
#define ENTIRE_DISP_ON() OLED_WriteCmd(0xA5)
#define ENTIRE_DISP_OFF() OLED_WriteCmd(0xA4)
/*
阴码显示或者阳码显示
*/
#define DISP_NORMAL() OLED_WriteCmd(0xA6)
#define DISP_INVERSE() OLED_WriteCmd(0xA7)
/*
打开显示或者关闭显示
*/
#define DISP_ON() OLED_WriteCmd(0xAF)
#define DISP_OFF() OLED_WriteCmd(0xAE)
/* 2. 滚动命令功能函数 */
typedef enum
{
H_RIGHT = 0x26,
H_LEFT = 0x27,
}H_SCROLL_DIR; // 水平滚动方向
typedef enum
{
HV_RIGHT = 0x29,
HV_LEFT = 0x2A,
}HV_SCROLL_DIR; // 水平和垂直滚动的方向
/*
开始或者停止滚动
*/
#define SCROLL_ON() OLED_WriteCmd(0x2F)
#define SCROLL_OFF() OLED_WriteCmd(0x2E)
/* 3. 地址设置功能函数 */
typedef enum
{
H_ADDR_MODE = 0, // 水平地址模式
V_ADDR_MODE = 1, // 垂直地址模式
PAGE_ADDR_MODE = 2, // 页地址模式
}MEM_MODE; // 内存地址模式
/*
行地址翻转或者不翻转
*/
#define OLED_SEG_REMAP() OLED_WriteCmd(0xA1)
#define OLED_SEG_NOREMAP() OLED_WriteCmd(0xA0)
/*
COM引脚扫描方向正常扫描或者逆扫描
*/
#define OLED_SCAN_NORMAL() OLED_WriteCmd(0xC0)
#define OLED_SCAN_REMAP() OLED_WriteCmd(0xC8)
typedef enum
{
COM_PIN_SEQ = 0,
COM_PIN_ALT = 1,
}COM_PINS_MODE; // COM引脚属性
typedef enum
{
COM_NOREMAP = 0,
COM_REMAP = 1,
}COM_REMAP_STATE; // COM引脚翻转
typedef enum
{
LEVEL_0 = 0x00,
LEVEL_1 = 0x20,
LEVEL_2 = 0x30,
}VCOMH_LEVEL; // 电压等级
/* 6. 电荷碰撞功能函数 */
typedef enum
{
PUMP_DISABLE = 0,
PUMP_ENABLE = 1,
}CHARGE_PUMP_STATE; // 打开或者关闭电荷泵
#define OELD_I2C_ADDR 0x78
#define OLED_TIMEOUT 500
extern I2C_HandleTypeDef hi2c1;
static I2C_HandleTypeDef *g_pHI2COLED = &hi2c1;
/*
* 函数名:OLED_WriteCmd
* 功能描述:I2C发送命令给OLED
* 输入参数:cmd-发送给OLED的命令
* 输出参数:无
* 返回值:0-成功, 其他值失败
*/
static int OLED_WriteCmd(uint8_t cmd)
{
uint8_t tmpbuf[2];
tmpbuf[0] = 0;
tmpbuf[1] = cmd;
return HAL_I2C_Master_Transmit(g_pHI2COLED, OELD_I2C_ADDR, tmpbuf, 2, OLED_TIMEOUT);
}
/*
* 函数名:OLED_WriteData
* 功能描述:I2C发送命令给OLED
* 输入参数:data-发送给OLED的写入GDRAM的数据
* 输出参数:无
* 返回值:0-成功, 其他值失败
*/
static int OLED_WriteData(uint8_t data)
{
uint8_t tmpbuf[2];
tmpbuf[0] = 0x40;
tmpbuf[1] = data;
return HAL_I2C_Master_Transmit(g_pHI2COLED, OELD_I2C_ADDR, tmpbuf, 2, OLED_TIMEOUT);
}
/*
* 函数名:OLED_WriteNBytes
* 功能描述:连续发送N个写入显存的数据
* 输入参数:buf - 数据buffer
* length - 数据个数
* 输出参数:无
* 返回值:0-成功, 其他值失败
*/
static int OLED_WriteNBytes(uint8_t *buf, uint16_t length)
{
return HAL_I2C_Mem_Write(g_pHI2COLED, OELD_I2C_ADDR, 0x40, 1, buf, length, OLED_TIMEOUT);
}
/************** 1. 基础命令功能函数 **************/
/*
* 函数名:OLED_SetContrastValue
* 功能描述:设置OLED的对比度:发送命令0x81--->发送对比度值
* 输入参数:value --> 对比度值,范围0~255
* 输出参数:无
* 返回值:无
*/
static void OLED_SetContrastValue(uint8_t value)
{
OLED_WriteCmd(0x81);
OLED_WriteCmd(value);
}
/************** 2. 滚动命令功能函数 **************/
/*
* 函数名:OLED_H_Scroll
* 功能描述:让指定页的像素朝着设置的方向按设置的频率水平滚动
* 输入参数:dir--->滚动方向:朝左或者朝右
* start--->起始页
* fr_time--->滚动间隔时间,每隔这么多帧水平偏移一列滚动
* end--->结束页
* 输出参数:无
* 返回值:无
*/
static void OLED_H_Scroll(H_SCROLL_DIR dir, uint8_t start, uint8_t fr_time, uint8_t end)
{
if((dir != H_RIGHT) && (dir != H_LEFT)) return;
if((start>0x07) || (fr_time>0x07) || (end>0x07)) return;
OLED_WriteCmd(dir);
OLED_WriteCmd(0x00);
OLED_WriteCmd(start);
OLED_WriteCmd(fr_time);
OLED_WriteCmd(end);
OLED_WriteCmd(0x00);
OLED_WriteCmd(0xFF);
}
/*
* 函数名:OLED_HV_Scroll
* 功能描述:让指定页的像素朝着设置的方向按设置的频率水平滚动且会将像素的行地址进行偏移offset这么多个单位
* 输入参数:dir--->滚动方向:朝左或者朝右
* start--->起始页
* fr_time--->滚动间隔时间,每隔这么多帧水平偏移一列滚动
* end--->结束页
* offset--->行偏移单位
* 输出参数:无
* 返回值:无
*/
static void OLED_HV_Scroll(HV_SCROLL_DIR dir, uint8_t start, uint8_t fr_time, uint8_t end, uint8_t offset)
{
if((dir != HV_RIGHT) && (dir != HV_LEFT)) return;
if((start>0x07) || (fr_time>0x07) || (end>0x07) || (offset>0x3F)) return;
OLED_WriteCmd(dir);
OLED_WriteCmd(0x00);
OLED_WriteCmd(start);
OLED_WriteCmd(fr_time);
OLED_WriteCmd(end);
OLED_WriteCmd(offset);
}
/*
* 函数名:OLED_SetVScrollArea
* 功能描述:设置OLED像素垂直滚动的区域
* 输入参数:area-->顶行区域
* row_num--->滚动的行区域
* 输出参数:无
* 返回值:无
*/
static void OLED_SetVScrollArea(uint8_t area, uint8_t row_num)
{
if((area>0x3F) || (row_num>0x7F)) return;
OLED_WriteCmd(0xA3);
OLED_WriteCmd(area);
OLED_WriteCmd(row_num);
}
/************** 3. 地址设置功能函数 **************/
static MEM_MODE mem_mode = PAGE_ADDR_MODE; // 静态局部变量,保存OLED的地址模式的
/*
* 函数名:OLED_SetColAddr_PAGE
* 功能描述:设置OLED在页地址模式下的显示起始行地址
* 输入参数:addr-->起始行地址
* 输出参数:无
* 返回值:无
*/
static void OLED_SetColAddr_PAGE(uint8_t addr)
{
if(mem_mode != PAGE_ADDR_MODE) return;
if(addr > 0x7F) return;
OLED_WriteCmd(0x00 + (addr & 0x0F));
OLED_WriteCmd(0x10 + (addr>>4));
}
/*
* 函数名:OLED_SetMemAddrMode
* 功能描述:设置OLED的地址模式
* 输入参数:mode-->地址模式:页地址模式、水平地址模式、垂直地址模式
* 输出参数:无
* 返回值:无
*/
static void OLED_SetMemAddrMode(MEM_MODE mode)
{
if((mode != H_ADDR_MODE) && (mode != V_ADDR_MODE) && (mode != PAGE_ADDR_MODE)) return;
OLED_WriteCmd(0x20);
OLED_WriteCmd(mode);
mem_mode = mode;
}
/*
* 函数名:OLED_SetColAddr_HV
* 功能描述:设置OLED在水平地址模式或垂直地址模式下像素显示的起始行地址和结束行地址
* 输入参数:start-->起始行地址
* end --->结束行地址
* 输出参数:无
* 返回值:无
*/
static void OLED_SetColAddr_HV(uint8_t start, uint8_t end)
{
if(mem_mode == PAGE_ADDR_MODE) return;
if((start > 127) || (end > 127)) return;
OLED_WriteCmd(0x21);
OLED_WriteCmd(start);
OLED_WriteCmd(end);
}
/*
* 函数名:OLED_SetPageAddr_HV
* 功能描述:设置OLED在水平地址模式或垂直地址模式下像素显示的起始页地址和结束页地址
* 输入参数:start-->起始页地址
* end --->结束页地址
* 输出参数:无
* 返回值:无
*/
static void OLED_SetPageAddr_HV(uint8_t start, uint8_t end)
{
if(mem_mode == PAGE_ADDR_MODE) return;
if((start > 7) || (end > 7)) return;
OLED_WriteCmd(0x22);
OLED_WriteCmd(start);
OLED_WriteCmd(end);
}
/*
* 函数名:OLED_SetPageAddr_PAGE
* 功能描述:设置OLED在页地址模式下的显示起始页地址
* 输入参数:addr-->起始页地址
* 输出参数:无
* 返回值:无
*/
static void OLED_SetPageAddr_PAGE(uint8_t addr)
{
if(mem_mode != PAGE_ADDR_MODE) return;
if(addr > 7) return;
OLED_WriteCmd(0xB0 + addr);
}
/************** 4. 硬件配置功能函数 **************/
/*
* 函数名:OLED_SetDispStartLine
* 功能描述:设置OLED从第line行开始显示,即将默认的0行偏移至line那一行
* 输入参数:line-->显示的起始行
* 输出参数:无
* 返回值:无
*/
static void OLED_SetDispStartLine(uint8_t line)
{
if(line > 63) return;
OLED_WriteCmd(0x40 + line);
}
/*
* 函数名:OLED_SetMuxRatio
* 功能描述:设置OLED复用率
* 输入参数:ratio-->复用率值
* 输出参数:无
* 返回值:无
*/
static void OLED_SetMuxRatio(uint8_t ratio)
{
if((ratio < 15) || (ratio > 63)) return;
OLED_WriteCmd(0xA8);
OLED_WriteCmd(ratio);
}
/*
* 函数名:OLED_SetDispOffset
* 功能描述:设置OLED的COM引脚偏移值
* 输入参数:offset-->COM偏移值
* 输出参数:无
* 返回值:无
*/
static void OLED_SetDispOffset(uint8_t offset)
{
if(offset > 63) return;
OLED_WriteCmd(0xD3);
OLED_WriteCmd(offset);
}
/*
* 函数名:OLED_SetComConfig
* 功能描述:设置OLED的COM引脚属性
* 输入参数:mode-->COM引脚模式是连续的还是可选择的
* state-->COM引脚翻转与否
* 输出参数:无
* 返回值:无
*/
static void OLED_SetComConfig(COM_PINS_MODE mode, COM_REMAP_STATE state)
{
if((mode != COM_PIN_SEQ) && (mode != COM_PIN_ALT)) return;
if((state != COM_NOREMAP) && (state != COM_REMAP)) return;
OLED_WriteCmd(0xDA);
OLED_WriteCmd(0x02 + (mode << 4) + (state << 5));
}
/************** 5. 时间设置功能函数 **************/
/*
* 函数名:OLED_SetDCLK_Freq
* 功能描述:设置OLED的扫描周期和晶振频率
* 输入参数:div-->时钟分频系数
* freq-->晶振频率
* 输出参数:无
* 返回值:无
*/
static void OLED_SetDCLK_Freq(uint8_t div, uint8_t freq)
{
if((div>0x0F) || (freq>0x0F)) return;
OLED_WriteCmd(0xD5);
OLED_WriteCmd(div + (freq<<4));
}
/*
* 函数名:OLED_SetPreChargePeriod
* 功能描述:设置OLED的预充电周期
* 输入参数:phase1-->预充电阶段1时间
* phase2-->预充电阶段2时间
* 输出参数:无
* 返回值:无
*/
static void OLED_SetPreChargePeriod(uint8_t phase1, uint8_t phase2)
{
if((phase1>0x0F) || (phase2>0x0F)) return;
OLED_WriteCmd(0xD9);
OLED_WriteCmd(phase1 + (phase2<<4));
}
/*
* 函数名:OLED_SetVcomhLevel
* 功能描述:设置OLED的电压等级
* 输入参数:level-->电压等级
* 输出参数:无
* 返回值:无
*/
static void OLED_SetVcomhLevel(VCOMH_LEVEL level)
{
if((level != LEVEL_0) && (level != LEVEL_1) && (level != LEVEL_2)) return;
OLED_WriteCmd(0xDB);
OLED_WriteCmd(level);
}
/************** 6. 电荷碰撞功能函数 **************/
/*
* 函数名:OLED_SetChargePump
* 功能描述:打开或关闭OLED的电荷泵
* 输入参数:state-->电荷泵打开或关闭
* 输出参数:无
* 返回值:无
*/
static void OLED_SetChargePump(CHARGE_PUMP_STATE state)
{
if((state != PUMP_DISABLE) && (state != PUMP_ENABLE)) return;
OLED_WriteCmd(0x8D);
OLED_WriteCmd((state<<2) + 0x10);
}
/************** 7. 初始化函数 **************/
/*
* 函数名:OLED_Init
* 功能描述:初始化OLED
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void OLED_Init(void)
{
/*
* 前提: 已经初始化的I2C通道
* 本工程里已经:
* 使用MX_I2C1_Init初始化I2C通道
* 使用HAL_I2C_MspInit初始化I2C引脚
*/
OLED_SetMemAddrMode(PAGE_ADDR_MODE); // 0. 设置地址模式
OLED_SetMuxRatio(0x3F); // 1. 设置多路复用率
OLED_SetDispOffset(0x00); // 2. 设置显示的偏移值
OLED_SetDispStartLine(0x00); // 3. 设置起始行
OLED_SEG_REMAP(); // 4. 行翻转
OLED_SCAN_REMAP(); // 5. 正常扫描
OLED_SetComConfig(COM_PIN_SEQ, COM_NOREMAP); // 6. COM 引脚设置
OLED_SetContrastValue(0x7F); // 7. 设置对比度
ENTIRE_DISP_OFF(); // 8. 全屏点亮/熄灭
DISP_NORMAL(); // 9. 显示模式
OLED_SetDCLK_Freq(0x00, 0x08); // 10. 设置分频系数和频率增值
OLED_SetChargePump(PUMP_ENABLE); // 11. 使能电荷碰撞
OLED_SetComConfig(COM_PIN_ALT, COM_NOREMAP);
DISP_ON();
}
/************** 8. 基本驱动功能函数 **************/
/*
* 函数名:OLED_SetPosition
* 功能描述:设置像素显示的起始页和起始列地址
* 输入参数:page-->页地址模式下的起始页地址
* col-->页地址模式下的起始行地址
* 输出参数:无
* 返回值:无
*/
void OLED_SetPosition(uint8_t page, uint8_t col)
{
OLED_SetPageAddr_PAGE(page);
OLED_SetColAddr_PAGE(col);
}
/*
* 函数名:OLED_Clear
* 功能描述:清屏函数
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void OLED_Clear(void)
{
uint8_t i = 0;
uint8_t buf[128] = {0};
for(i=0; i<8; i++)
{
OLED_SetPosition(i, 0);
OLED_WriteNBytes(&buf[0], 128);
}
}
/*
* 函数名:OLED_PutChar
* 功能描述:显示一个字符
* 输入参数:x --> x坐标(0~15)
* y --> y坐标(0~7)
* c --> 显示的字符
* 输出参数:无
* 返回值:无
*/
void OLED_PutChar(uint8_t x, uint8_t y, char c)
{
uint8_t page = y;
uint8_t col = x*8;
if (y > 7 || x > 15)
return;
OLED_SetPosition(page, col);
OLED_WriteNBytes((uint8_t*)&ascii_font[c][0], 8);
OLED_SetPosition(page + 1, col);
OLED_WriteNBytes((uint8_t*)&ascii_font[c][8], 8);
}
/*
* 函数名:OLED_PrintString
* 功能描述:显示一个字符串
* 输入参数:x --> x坐标(0~15)
* y --> y坐标(0~7)
* str --> 显示的字符串
* 输出参数:无
* 返回值:打印了多少个字符
*/
int OLED_PrintString(uint8_t x, uint8_t y, const char *str)
{
int i = 0;
while (str[i])
{
OLED_PutChar(x, y, str[i]);
x++;
if(x > 15)
{
x = 0;
y += 2;
}
i++;
}
return i;
}
/*
* 函数名:OLED_ClearLine
* 功能描述:清除一行
* 输入参数:x - 从这里开始
* y - 清除这行
* 输出参数:无
* 返回值:无
*/
void OLED_ClearLine(uint8_t x, uint8_t y)
{
for (;x < 16; x++)
{
OLED_PutChar(x, y, ' ');
}
}
/*
* OLED_PrintHex
* 功能描述:以16进制显示数值
* 输入参数:x - x坐标(0~15)
* y - y坐标(0~7)
* val - 显示的数据
* pre - 非零时显示"0x"前缀
* 输出参数:无
* 返回值:打印了多少个字符
*/
int OLED_PrintHex(uint8_t x, uint8_t y, uint32_t val, uint8_t pre)
{
uint8_t hex_tab[]={'0','1','2','3','4','5','6','7',\
'8','9','a','b','c','d','e','f'};
int i, j;
char arr[11];
/* 提取数据 */
arr[0] = '0';
arr[1] = 'x';
arr[10] = '\0';
j = 2;
for (i = 7; i >= 0; i--)
{
arr[j] = hex_tab[(val >> (i*4)) & 0xf];
if ((j != 2) || (arr[j] != '0'))
{
j++;
}
}
if (j == 2)
j++;
arr[j] = '\0';
if (pre)
{
OLED_PrintString(x, y, arr);
return j;
}
else
{
OLED_PrintString(x, y, arr+2);
return j - 2;
}
}
/*
* OLED_PrintSignedVal
* 功能描述:以10进制显示一个数值
* 输入参数:x --> x坐标(0~15)
* y --> y坐标(0~7)
* val --> 显示的数据
* 输出参数:无
* 返回值:打印了多少个字符
*/
int OLED_PrintSignedVal(uint8_t x, uint8_t y, int32_t val)
{
char str[16];
char revert_str[16];
int i = 0, j = 0;
int mod;
if (val < 0)
{
str[i++] = '-';
val = 0 - val;
}
else if (val == 0)
{
str[i++] = '0';
}
while (val)
{
mod = val % 10;
revert_str[j++] = mod + '0';
val = val / 10;
}
while (j)
{
str[i++] = revert_str[j-1];
j--;
}
str[i] = '\0';
OLED_PrintString(x, y, str);
return i;
}
/**********************************************************************
* 函数名称: OLED_Test
* 功能描述: OLED测试程序
* 输入参数: 无
* 输出参数: 无
* 无
* 返 回 值: 0 - 成功, 其他值 - 失败
***********************************************************************/
void OLED_Test(void)
{
int cnt = 0;
OLED_Init();
// 清屏
OLED_Clear();
while (1)
{
// 在(0, 0)打印'A'
OLED_PutChar(0, 0, 'A');
// 在(1, 0)打印'Y'
OLED_PutChar(1, 0, 'Y');
// 在第0列第2页打印一个字符串"Hello World!"
OLED_PrintString(0, 2, "Hello World!");
OLED_PrintSignedVal(0, 4, cnt++);
}
}
#ifndef __DRIVER_OLED_H
#define __DRIVER_OLED_H
#include <stdint.h>
void OLED_Init(void);
void OLED_SetPosition(uint8_t page, uint8_t col);
void OLED_Clear(void);
void OLED_PutChar(uint8_t x, uint8_t y, char c);
int OLED_PrintString(uint8_t x, uint8_t y, const char *str);
void OLED_ClearLine(uint8_t x, uint8_t y);
int OLED_PrintHex(uint8_t x, uint8_t y, uint32_t val, uint8_t pre);
int OLED_PrintSignedVal(uint8_t x, uint8_t y, int32_t val);
void OLED_Test(void);
#endif
我们的 driver_oled.c 的某些函数需要依靠字库,因此需要添加 ascii_font.c 文件:
#include <stdint.h>
const uint8_t ascii_font[128][16] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",1*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"",2*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",3*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",4*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",5*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",6*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",7*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",8*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",9*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",10*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",11*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",12*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",13*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",14*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",15*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",16*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",17*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",18*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",19*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",20*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",21*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",22*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",23*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",24*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",25*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",26*/
{0x00,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x3F,0x20,0x20,0x20,0x20,0x20,0x20},/*"",27*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"",28*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"",29*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"",30*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"",31*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",32*/
{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00},/*"!",33*/
{0x00,0x10,0x0C,0x02,0x10,0x0C,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*""",34*/
{0x00,0x40,0xC0,0x78,0x40,0xC0,0x78,0x00,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x00},/*"#",35*/
{0x00,0x70,0x88,0x88,0xFC,0x08,0x30,0x00,0x00,0x18,0x20,0x20,0xFF,0x21,0x1E,0x00},/*"$",36*/
{0xF0,0x08,0xF0,0x80,0x60,0x18,0x00,0x00,0x00,0x31,0x0C,0x03,0x1E,0x21,0x1E,0x00},/*"%",37*/
{0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x2C,0x19,0x27,0x21,0x10},/*"&",38*/
{0x00,0x12,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",39*/
{0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},/*"(",40*/
{0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},/*")",41*/
{0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},/*"*",42*/
{0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x0F,0x01,0x01,0x01},/*"+",43*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x70,0x00,0x00,0x00,0x00,0x00},/*",",44*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00},/*"-",45*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},/*".",46*/
{0x00,0x00,0x00,0x00,0xC0,0x38,0x04,0x00,0x00,0x60,0x18,0x07,0x00,0x00,0x00,0x00},/*"/",47*/
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},/*"0",48*/
{0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00},/*"1",49*/
{0x00,0x70,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},/*"2",50*/
{0x00,0x30,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x18,0x20,0x21,0x21,0x22,0x1C,0x00},/*"3",51*/
{0x00,0x00,0x80,0x40,0x30,0xF8,0x00,0x00,0x00,0x06,0x05,0x24,0x24,0x3F,0x24,0x24},/*"4",52*/
{0x00,0xF8,0x88,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x20,0x20,0x20,0x11,0x0E,0x00},/*"5",53*/
{0x00,0xE0,0x10,0x88,0x88,0x90,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x20,0x1F,0x00},/*"6",54*/
{0x00,0x18,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x00,0x00,0x3E,0x01,0x00,0x00,0x00},/*"7",55*/
{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},/*"8",56*/
{0x00,0xF0,0x08,0x08,0x08,0x10,0xE0,0x00,0x00,0x01,0x12,0x22,0x22,0x11,0x0F,0x00},/*"9",57*/
{0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},/*":",58*/
{0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00},/*";",59*/
{0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},/*"<",60*/
{0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x00},/*"=",61*/
{0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},/*">",62*/
{0x00,0x70,0x48,0x08,0x08,0x88,0x70,0x00,0x00,0x00,0x00,0x30,0x37,0x00,0x00,0x00},/*"?",63*/
{0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x28,0x2F,0x28,0x17,0x00},/*"@",64*/
{0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},/*"A",65*/
{0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},/*"B",66*/
{0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},/*"C",67*/
{0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},/*"D",68*/
{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},/*"E",69*/
{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},/*"F",70*/
{0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},/*"G",71*/
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},/*"H",72*/
{0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"I",73*/
{0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},/*"J",74*/
{0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},/*"K",75*/
{0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},/*"L",76*/
{0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x01,0x3E,0x01,0x3F,0x20,0x00},/*"M",77*/
{0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},/*"N",78*/
{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},/*"O",79*/
{0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},/*"P",80*/
{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x28,0x28,0x30,0x50,0x4F,0x00},/*"Q",81*/
{0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},/*"R",82*/
{0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},/*"S",83*/
{0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},/*"T",84*/
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},/*"U",85*/
{0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},/*"V",86*/
{0x08,0xF8,0x00,0xF8,0x00,0xF8,0x08,0x00,0x00,0x03,0x3E,0x01,0x3E,0x03,0x00,0x00},/*"W",87*/
{0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},/*"X",88*/
{0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},/*"Y",89*/
{0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},/*"Z",90*/
{0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},/*"[",91*/
{0x00,0x04,0x38,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},/*"\",92*/
{0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},/*"]",93*/
{0x00,0x00,0x04,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"^",94*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},/*"_",95*/
{0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",96*/
{0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x19,0x24,0x24,0x12,0x3F,0x20,0x00},/*"a",97*/
{0x10,0xF0,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},/*"b",98*/
{0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},/*"c",99*/
{0x00,0x00,0x80,0x80,0x80,0x90,0xF0,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},/*"d",100*/
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x24,0x24,0x24,0x24,0x17,0x00},/*"e",101*/
{0x00,0x80,0x80,0xE0,0x90,0x90,0x20,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"f",102*/
{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},/*"g",103*/
{0x10,0xF0,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},/*"h",104*/
{0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"i",105*/
{0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},/*"j",106*/
{0x10,0xF0,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x06,0x29,0x30,0x20,0x00},/*"k",107*/
{0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"l",108*/
{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},/*"m",109*/
{0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},/*"n",110*/
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},/*"o",111*/
{0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0x91,0x20,0x20,0x11,0x0E,0x00},/*"p",112*/
{0x00,0x00,0x00,0x80,0x80,0x00,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0x91,0xFF,0x80},/*"q",113*/
{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},/*"r",114*/
{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},/*"s",115*/
{0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x10,0x00},/*"t",116*/
{0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},/*"u",117*/
{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x03,0x0C,0x30,0x0C,0x03,0x00,0x00},/*"v",118*/
{0x80,0x80,0x00,0x80,0x80,0x00,0x80,0x80,0x01,0x0E,0x30,0x0C,0x07,0x38,0x06,0x01},/*"w",119*/
{0x00,0x80,0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x20,0x31,0x0E,0x2E,0x31,0x20,0x00},/*"x",120*/
{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x81,0x86,0x78,0x18,0x06,0x01,0x00},/*"y",121*/
{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},/*"z",122*/
{0x00,0x00,0x00,0x00,0x00,0xFC,0x02,0x02,0x00,0x00,0x00,0x00,0x01,0x3E,0x40,0x40},/*"{",123*/
{0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},/*"|",124*/
{0x02,0x02,0xFC,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x3E,0x01,0x00,0x00,0x00,0x00},/*"}",125*/
{0x00,0x02,0x01,0x02,0x02,0x04,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"~",126*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}/*"",127*/
};
接下来我们添加 driver_lcd.c 与 driver_lcd.h ,该文件的函数调用 driver_oled.c 中的函数,目的是以后我们换屏幕显示时,只需要修改 driver_lcd.c 与 driver_lcd.h 。
#include "driver_oled.h"
/*
* 函数名:LCD_Init
* 功能描述:初始化LCD
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void LCD_Init(void)
{
OLED_Init();
}
/*
* 函数名:LCD_Clear
* 功能描述:清屏函数
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void LCD_Clear(void)
{
OLED_Clear();
}
/*
* 函数名:LCD_PutChar
* 功能描述:显示一个字符
* 输入参数:x --> x坐标
* y --> y坐标
* c --> 显示的字符
* 输出参数:无
* 返回值:无
*/
void LCD_PutChar(uint8_t x, uint8_t y, char c)
{
OLED_PutChar(x, y, c);
}
/*
* 函数名:LCD_PrintString
* 功能描述:显示一个字符串
* 输入参数:x --> x坐标
* y --> y坐标
* str --> 显示的字符串
* 输出参数:无
* 返回值:打印了多少个字符
*/
int LCD_PrintString(uint8_t x, uint8_t y, const char *str)
{
return OLED_PrintString(x, y, str);
}
/*
* 函数名:OLED_ClearLine
* 功能描述:清除一行
* 输入参数:x - 从这里开始
* y - 清除这行
* 输出参数:无
* 返回值:无
*/
void LCD_ClearLine(uint8_t x, uint8_t y)
{
OLED_ClearLine(x, y);
}
/*
* LCD_PrintHex
* 功能描述:以16进制显示数值
* 输入参数:x - x坐标
* y - y坐标
* val - 显示的数据
* pre - 非零时显示"0x"前缀
* 输出参数:无
* 返回值:打印了多少个字符
*/
int LCD_PrintHex(uint8_t x, uint8_t y, uint32_t val, uint8_t pre)
{
return OLED_PrintHex(x, y, val, pre);
}
/*
* LCD_PrintSignedVal
* 功能描述:以10进制显示一个数值
* 输入参数:x --> x坐标(0~15)
* y --> y坐标(0~7)
* val --> 显示的数据
* 输出参数:无
* 返回值:打印了多少个字符
*/
int LCD_PrintSignedVal(uint8_t x, uint8_t y, int32_t val)
{
return OLED_PrintSignedVal(x, y, val);
}
/**********************************************************************
* 函数名称: LCD_Test
* 功能描述: LCD测试程序
* 输入参数: 无
* 输出参数: 无
* 无
* 返 回 值: 0 - 成功, 其他值 - 失败
***********************************************************************/
void LCD_Test(void)
{
OLED_Test();
}
#ifndef __DRIVER_LCD_H
#define __DRIVER_LCD_H
#include <stdint.h>
void LCD_Init(void);
void LCD_Clear(void);
void LCD_PutChar(uint8_t x, uint8_t y, char c);
int LCD_PrintString(uint8_t x, uint8_t y, const char *str);
void LCD_ClearLine(uint8_t x, uint8_t y);
int LCD_PrintHex(uint8_t x, uint8_t y, uint32_t val, uint8_t pre);
int LCD_PrintSignedVal(uint8_t x, uint8_t y, int32_t val);
void LCD_Test(void);
#endif
7 添加任务
7.1 添加LED闪烁任务
在 freertos.c 的文件中找到 StartDefaultTask 函数,并在函数中添加LED闪烁任务,如下图所示:
7.2 添加OLED任务
在 freertos.c 文件的指定区域创建OLED任务,如下图所示:
xTaskCreate(MyTask,"myfirsttask",128,NULL,osPriorityAboveNormal,NULL);
编写任务代码:
void MyTask(void *argument)
{
while(1)
{
LCD_Test();
}
}
8 编译运行
将编写的程序烧录到开发板中,可以看到两个任务同时运行。