1.基本内容:
设计一个放置在桌面使用的台灯,使用220v交流电供电。具备显示屏能够实时显示日期(年、月、日和星期),时间(小时、分钟、秒)和温度(摄氏度);能够通过语音交互播报实时日期、时间或者温度;能够通过语音交互控制桌面台灯的开启与关闭(或者明暗程度)。设计符合安全规范,符合日常家居使用习惯,操作简便,符合人性化需求。
2.演示视频
桌面指针式时钟与语音台灯设计
3.设计准备
LD3320语音识别模块、SYN6288语音合成模块、DHT11温湿度传感器模块+SL-Link下载器、STM32F103VET6、杜邦线、ILI9341液晶屏(野火stm32f103可一起购买)。
4.代码讲解
syn6288.c
#include "syn6288.h"
#include "stdarg.h"
xSYN6288_TypeDef xSYN6288; // 全局变量结构体
static void delay_ms(uint32_t ms) // 简单的延时函数
{
uint32_t i = 0;
ms = ms * 11993;
for (; i < ms; i++);
}
//Music:选择背景音乐。0:无背景音乐,1~15:选择背景音乐
// SYN6288_SendFrameInfo(0, "[v10][m1][t5]结算金额 为32.8元");
// 参数: 0~15 : 背景音乐,0_无背景音乐,1~15_背景音乐可选
// [V0~16]: 文本朗读音量,0_最小,16_最大
// [m0~16]: 背景音乐音量,0_最小,16_最大
// [t0~5]: 朗读语速,0_最慢,5_最快
// 其它不常用功能请参考数据手册
static void SYN6288_SendFrameInfo(uint8_t Music, uint8_t *HZdata)
{
/****************需要发送的文本**********************************/
unsigned char Frame_Info[50];
unsigned char HZ_Length;
unsigned char ecc = 0; //定义校验字节
unsigned int i = 0;
HZ_Length = strlen((char *)HZdata); //需要发送文本的长度
/*****************帧固定配置信息**************************************/
Frame_Info[0] = 0xFD ; //构造帧头FD
Frame_Info[1] = 0x00 ; //构造数据区长度的高字节
Frame_Info[2] = HZ_Length + 3; //构造数据区长度的低字节
Frame_Info[3] = 0x01 ; //构造命令字:合成播放命令
Frame_Info[4] = 0x01 | Music << 4 ; //构造命令参数:背景音乐设定
/*******************校验码计算***************************************/
for (i = 0; i < 5; i++) //依次发送构造好的5个帧头字节
ecc = ecc ^ (Frame_Info[i]); //对发送的字节进行异或校验
for (i = 0; i < HZ_Length; i++) //依次发送待合成的文本数据
ecc = ecc ^ (HZdata[i]); //对发送的字节进行异或校验
/*******************发送帧信息***************************************/
memcpy(&Frame_Info[5], HZdata, HZ_Length);
Frame_Info[5 + HZ_Length] = ecc;
if (xSYN6288.USARTx == USART1) USART1_SendData(Frame_Info, 5 + HZ_Length + 1);
if (xSYN6288.USARTx == USART2) USART2_SendData(Frame_Info, 5 + HZ_Length + 1);
if (xSYN6288.USARTx == USART3) USART3_SendData(Frame_Info, 5 + HZ_Length + 1);
if (xSYN6288.USARTx == UART4) UART4_SendData(Frame_Info, 5 + HZ_Length + 1);
if (xSYN6288.USARTx == UART5) UART5_SendData(Frame_Info, 5 + HZ_Length + 1);
}
/***********************************************************
* 名 称: SYN6288_Set(uint8_t *Info_data)
* 功 能: 主函数 程序入口
* 入口参数: *Info_data:固定的配置信息变量
* 出口参数:
* 说 明:本函数用于配置,停止合成、暂停合成等设置 ,默认波特率9600bps。
* 调用方法:通过调用已经定义的相关数组进行配置。
**********************************************************/
static void SYN6288_Set(uint8_t *Info_data)
{
uint8_t Com_Len;
Com_Len = strlen((char *)Info_data);
UART5_SendData(Info_data, Com_Len);
}
/******************************************************************************
* 函 数: SYN6288_Say
* 功 能: 输出合成语音
* 参 数: 格式化参数,如printf参数般一样的用法
* 返回值: 无
* 示 例: SYN6288_Say("你好吗?");
*******************************************************************************/
void SYN6288_Say(char *fmt, ...)
{
static char str_1[200]; // 缓存区1,模块每次可转换200字节
static char str_2[200]; // 缓存区2,模块每次可转换200字节
va_list ap;
va_start(ap, fmt);
vsprintf(str_1, fmt, ap);
va_end(ap);
sprintf(str_2, "[d][V12][m15][t5]%s", str_1); // [d]恢复默认状态,[V12]朗读音量0~16,[m15]背景音量0~16,[t5]语速0~5
SYN6288_SendFrameInfo(0, (uint8_t *)str_2); // 无背景音乐
}
/******************************************************************************
* 函 数: SYN6288_Init
* 功 能: 初始化所用串口, 模块默认通信波特率9600
* 参 数: 串口-USARTx
* 返回值: 无
* 示 例: SYN6288_Init(USART1);
*******************************************************************************/
void SYN6288_Init(USART_TypeDef *USARTx)
{
uint16_t baudrate = 9600; // 默认波特率9600bps。
delay_ms(200); // 上电后,稍作延时,等待模块进入稳定状态
if (USARTx == USART1) USART1_Init(baudrate);
if (USARTx == USART2) USART2_Init(baudrate);
if (USARTx == USART3) USART3_Init(baudrate);
#ifdef STM32F10X_HD
if (USARTx == UART4) UART4_Init(baudrate);
if (USARTx == UART5) UART5_Init(baudrate);
#endif
xSYN6288.FlagOkay = 0; // 初始化状态
xSYN6288.USARTx = USARTx; // 记录所用串口端口
}
DHT11.c
#include "DTH11.h"
#include "bsp_systick.h"
#include "stdio.h"
static void DHT11_GPIO_Config ( void );
static void DHT11_Mode_IPU ( void );
static void DHT11_Mode_Out_PP ( void );
static uint8_t DHT11_ReadByte ( void );
/**
* @brief DHT11 初始化函数
* @param 无
* @retval 无
*/
void DHT11_Init ( void )
{
DHT11_GPIO_Config ();
DHT11_Dout_1; // 拉高GPIOB10
}
/*
* 函数名:DHT11_GPIO_Config
* 描述 :配置DHT11用到的I/O口
* 输入 :无
* 输出 :无
*/
static void DHT11_GPIO_Config ( void )
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启DHT11_Dout_GPIO_PORT的外设时钟*/
DHT11_Dout_SCK_APBxClock_FUN ( DHT11_Dout_GPIO_CLK, ENABLE );
/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
GPIO_Init ( DHT11_Dout_GPIO_PORT, &GPIO_InitStructure );
}
/*
* 函数名:DHT11_Mode_IPU
* 描述 :使DHT11-DATA引脚变为上拉输入模式
* 输入 :无
* 输出 :无
*/
static void DHT11_Mode_IPU(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;
/*设置引脚模式为浮空输入模式*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure);
}
/*
* 函数名:DHT11_Mode_Out_PP
* 描述 :使DHT11-DATA引脚变为推挽输出模式
* 输入 :无
* 输出 :无
*/
static void DHT11_Mode_Out_PP(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure);
}
/*
* 从DHT11读取一个字节,MSB先行
*/
static uint8_t DHT11_ReadByte ( void )
{
uint8_t i, temp=0;
for(i=0;i<8;i++)
{
/*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/
while(DHT11_Dout_IN()==Bit_RESET);
/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
*通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时
*/
Systick_Delay_us(40); //延时x us 这个延时需要大于数据0持续的时间即可
if(DHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
{
/* 等待数据1的高电平结束 */
while(DHT11_Dout_IN()==Bit_SET);
temp|=(uint8_t)(0x01<<(7-i)); //把第7-i位置1,MSB先行
}
else // x us后为低电平表示数据“0”
{
temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
}
}
return temp;
}
/*
* 一次完整的数据传输为40bit,高位先出
* 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和
*/
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{
/*输出模式*/
DHT11_Mode_Out_PP();
/*主机拉低*/
DHT11_Dout_0;
/*延时18ms*/
Systick_Delay_ms(18);
/*总线拉高 主机延时30us*/
DHT11_Dout_1;
Systick_Delay_us(30); //延时30us
/*主机设为输入 判断从机响应信号*/
DHT11_Mode_IPU();
/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/
if(DHT11_Dout_IN()==Bit_RESET)
{
/*轮询直到从机发出 的80us 低电平 响应信号结束*/
while(DHT11_Dout_IN()==Bit_RESET);
/*轮询直到从机发出的 80us 高电平 标置信号结束*/
while(DHT11_Dout_IN()==Bit_SET);
/*开始接收数据*/
DHT11_Data->humi_int= DHT11_ReadByte();
DHT11_Data->humi_deci= DHT11_ReadByte();
DHT11_Data->temp_int= DHT11_ReadByte();
DHT11_Data->temp_deci= DHT11_ReadByte();
DHT11_Data->check_sum= DHT11_ReadByte();
/*读取结束,引脚改为输出模式*/
DHT11_Mode_Out_PP();
/*主机拉高*/
DHT11_Dout_1;
/*检查读取的数据是否正确*/
if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
return SUCCESS;
else
return ERROR;
}
else
return ERROR;
}
ili9341.c
/**
******************************************************************************
* @file bsp_ili9341_lcd.c
* @version V1.0
* @date 2013-xx-xx
* @brief ili9341液晶屏驱动
******************************************************************************
* @attention
*
* 实验平台:秉火 F103-指南者 STM32 开发板
* 论坛 :http://www.firebbs.cn
* 淘宝 :http://firestm32.taobao.com
*
******************************************************************************
*/
#include "./lcd/bsp_ili9341_lcd.h"
#include "./font/fonts.h"
#include <math.h>
//根据液晶扫描方向而变化的XY像素宽度
//调用ILI9341_GramScan函数设置方向时会自动更改
uint16_t LCD_X_LENGTH = ILI9341_LESS_PIXEL;
uint16_t LCD_Y_LENGTH = ILI9341_MORE_PIXEL;
//液晶屏扫描模式,本变量主要用于方便选择触摸屏的计算参数
//参数可选值为0-7
//调用ILI9341_GramScan函数设置方向时会自动更改
//LCD刚初始化完成时会使用本默认值
uint8_t LCD_SCAN_MODE = 6;
/**
* @brief 向ILI9341写入命令
* @param usCmd :要写入的命令(表寄存器地址)
* @retval 无
*/
__inline void ILI9341_Write_Cmd ( uint16_t usCmd )
{
* ( __IO uint16_t * ) ( FSMC_Addr_ILI9341_CMD ) = usCmd;
}
/**
* @brief 向ILI9341写入数据
* @param usData :要写入的数据
* @retval 无
*/
__inline void ILI9341_Write_Data ( uint16_t usData )
{
* ( __IO uint16_t * ) ( FSMC_Addr_ILI9341_DATA ) = usData;
}
/**
* @brief 从ILI9341读取数据
* @param 无
* @retval 读取到的数据
*/
__inline uint16_t ILI9341_Read_Data ( void )
{
return ( * ( __IO uint16_t * ) ( FSMC_Addr_ILI9341_DATA ) );
}
/**
* @brief 用于 ILI9341 简单延时函数
* @param nCount :延时计数值
* @retval 无
*/
static void ILI9341_Delay ( __IO uint32_t nCount )
{
for ( ; nCount != 0; nCount -- );
}
/**
* @brief 初始化ILI9341的IO引脚
* @param 无
* @retval 无
*/
static void ILI9341_GPIO_Config ( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能FSMC对应相应管脚时钟*/
RCC_APB2PeriphClockCmd (
/*控制信号*/
ILI9341_CS_CLK|ILI9341_DC_CLK|ILI9341_WR_CLK|
ILI9341_RD_CLK |ILI9341_BK_CLK|ILI9341_RST_CLK|
/*数据信号*/
ILI9341_D0_CLK|ILI9341_D1_CLK| ILI9341_D2_CLK |
ILI9341_D3_CLK | ILI9341_D4_CLK|ILI9341_D5_CLK|
ILI9341_D6_CLK | ILI9341_D7_CLK|ILI9341_D8_CLK|
ILI9341_D9_CLK | ILI9341_D10_CLK|ILI9341_D11_CLK|
ILI9341_D12_CLK | ILI9341_D13_CLK|ILI9341_D14_CLK|
ILI9341_D15_CLK , ENABLE );
/* 配置FSMC相对应的数据线,FSMC-D0~D15 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = ILI9341_D0_PIN;
GPIO_Init ( ILI9341_D0_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D1_PIN;
GPIO_Init ( ILI9341_D1_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D2_PIN;
GPIO_Init ( ILI9341_D2_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D3_PIN;
GPIO_Init ( ILI9341_D3_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D4_PIN;
GPIO_Init ( ILI9341_D4_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D5_PIN;
GPIO_Init ( ILI9341_D5_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D6_PIN;
GPIO_Init ( ILI9341_D6_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D7_PIN;
GPIO_Init ( ILI9341_D7_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D8_PIN;
GPIO_Init ( ILI9341_D8_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D9_PIN;
GPIO_Init ( ILI9341_D9_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D10_PIN;
GPIO_Init ( ILI9341_D10_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D11_PIN;
GPIO_Init ( ILI9341_D11_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D12_PIN;
GPIO_Init ( ILI9341_D12_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D13_PIN;
GPIO_Init ( ILI9341_D13_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D14_PIN;
GPIO_Init ( ILI9341_D14_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_D15_PIN;
GPIO_Init ( ILI9341_D15_PORT, & GPIO_InitStructure );
/* 配置FSMC相对应的控制线
* FSMC_NOE :LCD-RD
* FSMC_NWE :LCD-WR
* FSMC_NE1 :LCD-CS
* FSMC_A16 :LCD-DC
*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = ILI9341_RD_PIN;
GPIO_Init (ILI9341_RD_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_WR_PIN;
GPIO_Init (ILI9341_WR_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_CS_PIN;
GPIO_Init ( ILI9341_CS_PORT, & GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = ILI9341_DC_PIN;
GPIO_Init ( ILI9341_DC_PORT, & GPIO_InitStructure );
/* 配置LCD复位RST控制管脚*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = ILI9341_RST_PIN;
GPIO_Init ( ILI9341_RST_PORT, & GPIO_InitStructure );
/* 配置LCD背光控制管脚BK*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = ILI9341_BK_PIN;
GPIO_Init ( ILI9341_BK_PORT, & GPIO_InitStructure );
}
/**
* @brief LCD FSMC 模式配置
* @param 无
* @retval 无
*/
static void ILI9341_FSMC_Config ( void )
{
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
/* 使能FSMC时钟*/
RCC_AHBPeriphClockCmd ( RCC_AHBPeriph_FSMC, ENABLE );
//地址建立时间(ADDSET)为1个HCLK 2/72M=28ns
readWriteTiming.FSMC_AddressSetupTime = 0x01; //地址建立时间
//数据保持时间(DATAST)+ 1个HCLK = 5/72M=70ns
readWriteTiming.FSMC_DataSetupTime = 0x04; //数据建立时间
//选择控制的模式
//模式B,异步NOR FLASH模式,与ILI9341的8080时序匹配
readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_B;
/*以下配置与模式B无关*/
//地址保持时间(ADDHLD)模式A未用到
readWriteTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间
//设置总线转换周期,仅用于复用模式的NOR操作
readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
//设置时钟分频,仅用于同步类型的存储器
readWriteTiming.FSMC_CLKDivision = 0x00;
//数据保持时间,仅用于同步型的NOR
readWriteTiming.FSMC_DataLatency = 0x00;
FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait =FSMC_AsynchronousWait_Disable;
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAMx;
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR;
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &readWriteTiming;
FSMC_NORSRAMInit ( & FSMC_NORSRAMInitStructure );
/* 使能 FSMC_Bank1_NORSRAM4 */
FSMC_NORSRAMCmd ( FSMC_Bank1_NORSRAMx, ENABLE );
}
/**
* @brief 初始化ILI9341寄存器
* @param 无
* @retval 无
*/
static void ILI9341_REG_Config ( void )
{
/* Power control B (CFh) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xCF );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x81 );
ILI9341_Write_Data ( 0x30 );
/* Power on sequence control (EDh) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xED );
ILI9341_Write_Data ( 0x64 );
ILI9341_Write_Data ( 0x03 );
ILI9341_Write_Data ( 0x12 );
ILI9341_Write_Data ( 0x81 );
/* Driver timing control A (E8h) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xE8 );
ILI9341_Write_Data ( 0x85 );
ILI9341_Write_Data ( 0x10 );
ILI9341_Write_Data ( 0x78 );
/* Power control A (CBh) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xCB );
ILI9341_Write_Data ( 0x39 );
ILI9341_Write_Data ( 0x2C );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x34 );
ILI9341_Write_Data ( 0x02 );
/* Pump ratio control (F7h) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xF7 );
ILI9341_Write_Data ( 0x20 );
/* Driver timing control B */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xEA );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x00 );
/* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xB1 );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x1B );
/* Display Function Control (B6h) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xB6 );
ILI9341_Write_Data ( 0x0A );
ILI9341_Write_Data ( 0xA2 );
/* Power Control 1 (C0h) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xC0 );
ILI9341_Write_Data ( 0x35 );
/* Power Control 2 (C1h) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0xC1 );
ILI9341_Write_Data ( 0x11 );
/* VCOM Control 1 (C5h) */
ILI9341_Write_Cmd ( 0xC5 );
ILI9341_Write_Data ( 0x45 );
ILI9341_Write_Data ( 0x45 );
/* VCOM Control 2 (C7h) */
ILI9341_Write_Cmd ( 0xC7 );
ILI9341_Write_Data ( 0xA2 );
/* Enable 3G (F2h) */
ILI9341_Write_Cmd ( 0xF2 );
ILI9341_Write_Data ( 0x00 );
/* Gamma Set (26h) */
ILI9341_Write_Cmd ( 0x26 );
ILI9341_Write_Data ( 0x01 );
DEBUG_DELAY ();
/* Positive Gamma Correction */
ILI9341_Write_Cmd ( 0xE0 ); //Set Gamma
ILI9341_Write_Data ( 0x0F );
ILI9341_Write_Data ( 0x26 );
ILI9341_Write_Data ( 0x24 );
ILI9341_Write_Data ( 0x0B );
ILI9341_Write_Data ( 0x0E );
ILI9341_Write_Data ( 0x09 );
ILI9341_Write_Data ( 0x54 );
ILI9341_Write_Data ( 0xA8 );
ILI9341_Write_Data ( 0x46 );
ILI9341_Write_Data ( 0x0C );
ILI9341_Write_Data ( 0x17 );
ILI9341_Write_Data ( 0x09 );
ILI9341_Write_Data ( 0x0F );
ILI9341_Write_Data ( 0x07 );
ILI9341_Write_Data ( 0x00 );
/* Negative Gamma Correction (E1h) */
ILI9341_Write_Cmd ( 0XE1 ); //Set Gamma
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x19 );
ILI9341_Write_Data ( 0x1B );
ILI9341_Write_Data ( 0x04 );
ILI9341_Write_Data ( 0x10 );
ILI9341_Write_Data ( 0x07 );
ILI9341_Write_Data ( 0x2A );
ILI9341_Write_Data ( 0x47 );
ILI9341_Write_Data ( 0x39 );
ILI9341_Write_Data ( 0x03 );
ILI9341_Write_Data ( 0x06 );
ILI9341_Write_Data ( 0x06 );
ILI9341_Write_Data ( 0x30 );
ILI9341_Write_Data ( 0x38 );
ILI9341_Write_Data ( 0x0F );
/* memory access control set */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0x36 );
ILI9341_Write_Data ( 0xC8 ); /*竖屏 左上角到 (起点)到右下角 (终点)扫描方式*/
DEBUG_DELAY ();
/* column address control set */
ILI9341_Write_Cmd ( CMD_SetCoordinateX );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0xEF );
/* page address control set */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( CMD_SetCoordinateY );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x00 );
ILI9341_Write_Data ( 0x01 );
ILI9341_Write_Data ( 0x3F );
/* Pixel Format Set (3Ah) */
DEBUG_DELAY ();
ILI9341_Write_Cmd ( 0x3a );
ILI9341_Write_Data ( 0x55 );
/* Sleep Out (11h) */
ILI9341_Write_Cmd ( 0x11 );
ILI9341_Delay ( 0xAFFf<<2 );
DEBUG_DELAY ();
/* Display ON (29h) */
ILI9341_Write_Cmd ( 0x29 );
}
/**
* @brief ILI9341初始化函数,如果要用到lcd,一定要调用这个函数
* @param 无
* @retval 无
*/
void ILI9341_Init ( void )
{
ILI9341_GPIO_Config ();
ILI9341_FSMC_Config ();
ILI9341_BackLed_Control ( ENABLE ); //点亮LCD背光灯
ILI9341_Rst ();
ILI9341_REG_Config ();
//设置默认扫描方向,其中 6 模式为大部分液晶例程的默认显示方向
ILI9341_GramScan(LCD_SCAN_MODE);
}
/**
* @brief ILI9341背光LED控制
* @param enumState :决定是否使能背光LED
* 该参数为以下值之一:
* @arg ENABLE :使能背光LED
* @arg DISABLE :禁用背光LED
* @retval 无
*/
void ILI9341_BackLed_Control ( FunctionalState enumState )
{
if ( enumState )
GPIO_ResetBits ( ILI9341_BK_PORT, ILI9341_BK_PIN );
else
GPIO_SetBits ( ILI9341_BK_PORT, ILI9341_BK_PIN );
}
/**
* @brief ILI9341 软件复位
* @param 无
* @retval 无
*/
void ILI9341_Rst ( void )
{
GPIO_ResetBits ( ILI9341_RST_PORT, ILI9341_RST_PIN ); //低电平复位
ILI9341_Delay ( 0xAFF );
GPIO_SetBits ( ILI9341_RST_PORT, ILI9341_RST_PIN );
ILI9341_Delay ( 0xAFF );
}
/**
* @brief 设置ILI9341的GRAM的扫描方向
* @param ucOption :选择GRAM的扫描方向
* @arg 0-7 :参数可选值为0-7这八个方向
*
* !!!其中0、3、5、6 模式适合从左至右显示文字,
* 不推荐使用其它模式显示文字 其它模式显示文字会有镜像效果
*
* 其中0、2、4、6 模式的X方向像素为240,Y方向像素为320
* 其中1、3、5、7 模式下X方向像素为320,Y方向像素为240
*
* 其中 6 模式为大部分液晶例程的默认显示方向
* 其中 3 模式为摄像头例程使用的方向
* 其中 0 模式为BMP图片显示例程使用的方向
*
* @retval 无
* @note 坐标图例:A表示向上,V表示向下,<表示向左,>表示向右
X表示X轴,Y表示Y轴
------------------------------------------------------------
模式0: . 模式1: . 模式2: . 模式3:
A . A . A . A
| . | . | . |
Y . X . Y . X
0 . 1 . 2 . 3
<--- X0 o . <----Y1 o . o 2X---> . o 3Y--->
------------------------------------------------------------
模式4: . 模式5: . 模式6: . 模式7:
<--- X4 o . <--- Y5 o . o 6X---> . o 7Y--->
4 . 5 . 6 . 7
Y . X . Y . X
| . | . | . |
V . V . V . V
---------------------------------------------------------
LCD屏示例
|-----------------|
| 秉火Logo |
| |
| |
| |
| |
| |
| |
| |
| |
|-----------------|
屏幕正面(宽240,高320)
*******************************************************/
void ILI9341_GramScan ( uint8_t ucOption )
{
//参数检查,只可输入0-7
if(ucOption >7 )
return;
//根据模式更新LCD_SCAN_MODE的值,主要用于触摸屏选择计算参数
LCD_SCAN_MODE = ucOption;
//根据模式更新XY方向的像素宽度
if(ucOption%2 == 0)
{
//0 2 4 6模式下X方向像素宽度为240,Y方向为320
LCD_X_LENGTH = ILI9341_LESS_PIXEL;
LCD_Y_LENGTH = ILI9341_MORE_PIXEL;
}
else
{
//1 3 5 7模式下X方向像素宽度为320,Y方向为240
LCD_X_LENGTH = ILI9341_MORE_PIXEL;
LCD_Y_LENGTH = ILI9341_LESS_PIXEL;
}
//0x36命令参数的高3位可用于设置GRAM扫描方向
ILI9341_Write_Cmd ( 0x36 );
ILI9341_Write_Data ( 0x08 |(ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
ILI9341_Write_Cmd ( CMD_SetCoordinateX );
ILI9341_Write_Data ( 0x00 ); /* x 起始坐标高8位 */
ILI9341_Write_Data ( 0x00 ); /* x 起始坐标低8位 */
ILI9341_Write_Data ( ((LCD_X_LENGTH-1)>>8)&0xFF ); /* x 结束坐标高8位 */
ILI9341_Write_Data ( (LCD_X_LENGTH-1)&0xFF ); /* x 结束坐标低8位 */
ILI9341_Write_Cmd ( CMD_SetCoordinateY );
ILI9341_Write_Data ( 0x00 ); /* y 起始坐标高8位 */
ILI9341_Write_Data ( 0x00 ); /* y 起始坐标低8位 */
ILI9341_Write_Data ( ((LCD_Y_LENGTH-1)>>8)&0xFF ); /* y 结束坐标高8位 */
ILI9341_Write_Data ( (LCD_Y_LENGTH-1)&0xFF ); /* y 结束坐标低8位 */
/* write gram start */
ILI9341_Write_Cmd ( CMD_SetPixel );
}
/**
* @brief 在ILI9341显示器上开辟一个窗口
* @param usX :在特定扫描方向下窗口的起点X坐标
* @param usY :在特定扫描方向下窗口的起点Y坐标
* @param usWidth :窗口的宽度
* @param usHeight :窗口的高度
* @retval 无
*/
void ILI9341_OpenWindow ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{
ILI9341_Write_Cmd ( CMD_SetCoordinateX ); /* 设置X坐标 */
ILI9341_Write_Data ( usX >> 8 ); /* 先高8位,然后低8位 */
ILI9341_Write_Data ( usX & 0xff ); /* 设置起始点和结束点*/
ILI9341_Write_Data ( ( usX + usWidth - 1 ) >> 8 );
ILI9341_Write_Data ( ( usX + usWidth - 1 ) & 0xff );
ILI9341_Write_Cmd ( CMD_SetCoordinateY ); /* 设置Y坐标*/
ILI9341_Write_Data ( usY >> 8 );
ILI9341_Write_Data ( usY & 0xff );
ILI9341_Write_Data ( ( usY + usHeight - 1 ) >> 8 );
ILI9341_Write_Data ( ( usY + usHeight - 1) & 0xff );
}
/**
* @brief 设定ILI9341的光标坐标
* @param usX :在特定扫描方向下光标的X坐标
* @param usY :在特定扫描方向下光标的Y坐标
* @retval 无
*/
static void ILI9341_SetCursor ( uint16_t usX, uint16_t usY )
{
ILI9341_OpenWindow ( usX, usY, 1, 1 );
}
/**
* @brief 在ILI9341显示器上以某一颜色填充像素点
* @param ulAmout_Point :要填充颜色的像素点的总数目
* @param usColor :颜色
* @retval 无
*/
static __inline void ILI9341_FillColor ( uint32_t ulAmout_Point, uint16_t usColor )
{
uint32_t i = 0;
/* memory write */
ILI9341_Write_Cmd ( CMD_SetPixel );
for ( i = 0; i < ulAmout_Point; i ++ )
ILI9341_Write_Data ( usColor );
}
/**
* @brief 对ILI9341显示器的某一窗口以某种颜色进行清屏
* @param usX :在特定扫描方向下窗口的起点X坐标
* @param usY :在特定扫描方向下窗口的起点Y坐标
* @param usWidth :窗口的宽度
* @param usHeight :窗口的高度
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_Clear ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{
ILI9341_OpenWindow ( usX, usY, usWidth, usHeight );
ILI9341_FillColor ( usWidth * usHeight, CurrentBackColor );
}
/**
* @brief 对ILI9341显示器的某一点以某种颜色进行填充
* @param usX :在特定扫描方向下该点的X坐标
* @param usY :在特定扫描方向下该点的Y坐标
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_SetPointPixel ( uint16_t usX, uint16_t usY )
{
if ( ( usX < LCD_X_LENGTH ) && ( usY < LCD_Y_LENGTH ))
{
ILI9341_SetCursor ( usX, usY );
ILI9341_FillColor ( 1, CYAN );
}
}
/**
* @brief 对ILI9341显示器的某一点以某种颜色进行填充
* @param usX :在特定扫描方向下该点的X坐标
* @param usY :在特定扫描方向下该点的Y坐标
* @param fill:0和1选择填充前景色或背景色
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_SetPointPixel_Fill ( uint16_t usX, uint16_t usY ,uint8_t fill)
{
if ( ( usX < LCD_X_LENGTH ) && ( usY < LCD_Y_LENGTH )&& fill)
{
ILI9341_SetCursor ( usX, usY );
ILI9341_FillColor ( 1, CurrentTextColor );
}
else
{
ILI9341_SetCursor ( usX, usY );
ILI9341_FillColor ( 1, CurrentBackColor );
}
}
/**
* @brief 对ILI9341显示器的某一点以某种颜色进行填充
* @param usX :在特定扫描方向下该点的X坐标
* @param usY :在特定扫描方向下该点的Y坐标
* @param color:选择某种颜色进行填充
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_SetPointPixel_Color ( uint16_t usX, uint16_t usY ,uint16_t color)
{
if ( ( usX < LCD_X_LENGTH ) && ( usY < LCD_Y_LENGTH ))
{
ILI9341_SetCursor ( usX, usY );
ILI9341_FillColor ( 1, color );
}
}
/**
* @brief 读取ILI9341 GRAN 的一个像素数据
* @param 无
* @retval 像素数据
*/
static uint16_t ILI9341_Read_PixelData ( void )
{
uint16_t usR=0, usG=0, usB=0 ;
ILI9341_Write_Cmd ( 0x2E ); /* 读数据 */
usR = ILI9341_Read_Data (); /*FIRST READ OUT DUMMY DATA*/
usR = ILI9341_Read_Data (); /*READ OUT RED DATA */
usB = ILI9341_Read_Data (); /*READ OUT BLUE DATA*/
usG = ILI9341_Read_Data (); /*READ OUT GREEN DATA*/
return ( ( ( usR >> 11 ) << 11 ) | ( ( usG >> 10 ) << 5 ) | ( usB >> 11 ) );
}
/**
* @brief 获取 ILI9341 显示器上某一个坐标点的像素数据
* @param usX :在特定扫描方向下该点的X坐标
* @param usY :在特定扫描方向下该点的Y坐标
* @retval 像素数据
*/
uint16_t ILI9341_GetPointPixel ( uint16_t usX, uint16_t usY )
{
uint16_t usPixelData;
ILI9341_SetCursor ( usX, usY );
usPixelData = ILI9341_Read_PixelData ();
return usPixelData;
}
/**
* @brief 在 ILI9341 显示器上使用 Bresenham 算法画线段
* @param usX1 :在特定扫描方向下线段的一个端点X坐标
* @param usY1 :在特定扫描方向下线段的一个端点Y坐标
* @param usX2 :在特定扫描方向下线段的另一个端点X坐标
* @param usY2 :在特定扫描方向下线段的另一个端点Y坐标
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DrawLine ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 )
{
uint16_t us;
uint16_t usX_Current, usY_Current;
int32_t lError_X = 0, lError_Y = 0, lDelta_X, lDelta_Y, lDistance;
int32_t lIncrease_X, lIncrease_Y;
lDelta_X = usX2 - usX1; //计算坐标增量
lDelta_Y = usY2 - usY1;
usX_Current = usX1;
usY_Current = usY1;
if ( lDelta_X > 0 )
lIncrease_X = 1; //设置单步方向
else if ( lDelta_X == 0 )
lIncrease_X = 0;//垂直线
else
{
lIncrease_X = -1;
lDelta_X = - lDelta_X;
}
if ( lDelta_Y > 0 )
lIncrease_Y = 1;
else if ( lDelta_Y == 0 )
lIncrease_Y = 0;//水平线
else
{
lIncrease_Y = -1;
lDelta_Y = - lDelta_Y;
}
if ( lDelta_X > lDelta_Y )
lDistance = lDelta_X; //选取基本增量坐标轴
else
lDistance = lDelta_Y;
for ( us = 0; us <= lDistance + 1; us ++ )//画线输出
{
ILI9341_SetPointPixel ( usX_Current, usY_Current );//画点
lError_X += lDelta_X ;
lError_Y += lDelta_Y ;
if ( lError_X > lDistance )
{
lError_X -= lDistance;
usX_Current += lIncrease_X;
}
if ( lError_Y > lDistance )
{
lError_Y -= lDistance;
usY_Current += lIncrease_Y;
}
}
}
/*
函数功能:任意角度画直线
参 数:
usX,usY:坐标
usAngle :度数
usRadius:半径
usLength :线段的长度
c :颜色值 0或者1
例如:ILI9341_DrawAngleLine(60,30,45,20,20,1);//角度画线
*/
void ILI9341_DrawAngleLine( uint32_t usX, uint32_t usY, float usAngle, uint32_t usRadius, uint32_t usLength,uint16_t color)
{
int i;
int x0,y0;
double k=usAngle*(3.1415926535/180);
for(i=usRadius;i<usLength;i++)
{
x0=cos(k)*i;
y0=sin(k)*i;
ILI9341_SetPointPixel_Color(usX+x0,usY+y0,color);
}
}
/*
函数功能:任意角度画直线
参 数:
usX,usY:坐标
usRadius :度数
usLength :线段的长度
c :颜色值 0或者1
例如:OLED_DrawAngleLine(60,30,45,20,20,1);//角度画线
*/
void ILI9341_DrawAngleLine2( uint32_t usX, uint32_t usY,float usAngle,uint32_t usRadius,uint32_t usLength,uint8_t fill)
{
uint32_t i;
int x0,y0;
double k=usAngle*(3.1415926535L/180);
for(i=usRadius;i<usLength;i++)
{
x0=cos(k)*i;
y0=sin(k)*i;
ILI9341_SetPointPixel_Fill(usX+x0,usY+y0,fill);
}
}
/**
* @brief 在 ILI9341 显示器上画一个矩形
* @param usX_Start :在特定扫描方向下矩形的起始点X坐标
* @param usY_Start :在特定扫描方向下矩形的起始点Y坐标
* @param usWidth:矩形的宽度(单位:像素)
* @param usHeight:矩形的高度(单位:像素)
* @param ucFilled :选择是否填充该矩形
* 该参数为以下值之一:
* @arg 0 :空心矩形
* @arg 1 :实心矩形
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DrawRectangle ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint8_t ucFilled )
{
if ( ucFilled )
{
ILI9341_OpenWindow ( usX_Start, usY_Start, usWidth, usHeight );
ILI9341_FillColor ( usWidth * usHeight ,CurrentTextColor);
}
else
{
ILI9341_DrawLine ( usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start );
ILI9341_DrawLine ( usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );
ILI9341_DrawLine ( usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1 );
ILI9341_DrawLine ( usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );
}
}
/**
* @brief 在 ILI9341 显示器上使用 Bresenham 算法画圆
* @param usX_Center :在特定扫描方向下圆心的X坐标
* @param usY_Center :在特定扫描方向下圆心的Y坐标
* @param usRadius:圆的半径(单位:像素)
* @param ucFilled :选择是否填充该圆
* 该参数为以下值之一:
* @arg 0 :空心圆
* @arg 1 :实心圆
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DrawCircle ( uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled )
{
int16_t sCurrentX, sCurrentY;
int16_t sError;
sCurrentX = 0; sCurrentY = usRadius;
sError = 3 - ( usRadius << 1 ); //判断下个点位置的标志
while ( sCurrentX <= sCurrentY )
{
int16_t sCountY;
if ( ucFilled )
for ( sCountY = sCurrentX; sCountY <= sCurrentY; sCountY ++ )
{
ILI9341_SetPointPixel ( usX_Center + sCurrentX, usY_Center + sCountY ); //1,研究对象
ILI9341_SetPointPixel ( usX_Center - sCurrentX, usY_Center + sCountY ); //2
ILI9341_SetPointPixel ( usX_Center - sCountY, usY_Center + sCurrentX ); //3
ILI9341_SetPointPixel ( usX_Center - sCountY, usY_Center - sCurrentX ); //4
ILI9341_SetPointPixel ( usX_Center - sCurrentX, usY_Center - sCountY ); //5
ILI9341_SetPointPixel ( usX_Center + sCurrentX, usY_Center - sCountY ); //6
ILI9341_SetPointPixel ( usX_Center + sCountY, usY_Center - sCurrentX ); //7
ILI9341_SetPointPixel ( usX_Center + sCountY, usY_Center + sCurrentX ); //0
}
else
{
ILI9341_SetPointPixel ( usX_Center + sCurrentX, usY_Center + sCurrentY ); //1,研究对象
ILI9341_SetPointPixel ( usX_Center - sCurrentX, usY_Center + sCurrentY ); //2
ILI9341_SetPointPixel ( usX_Center - sCurrentY, usY_Center + sCurrentX ); //3
ILI9341_SetPointPixel ( usX_Center - sCurrentY, usY_Center - sCurrentX ); //4
ILI9341_SetPointPixel ( usX_Center - sCurrentX, usY_Center - sCurrentY ); //5
ILI9341_SetPointPixel ( usX_Center + sCurrentX, usY_Center - sCurrentY ); //6
ILI9341_SetPointPixel ( usX_Center + sCurrentY, usY_Center - sCurrentX ); //7
ILI9341_SetPointPixel ( usX_Center + sCurrentY, usY_Center + sCurrentX ); //0
}
sCurrentX ++;
if ( sError < 0 )
sError += 4 * sCurrentX + 6;
else
{
sError += 10 + 4 * ( sCurrentX - sCurrentY );
sCurrentY --;
}
}
}
/**
* @brief 在 ILI9341 显示器上显示一个英文字符
* @param usX :在特定扫描方向下字符的起始X坐标
* @param usY :在特定扫描方向下该点的起始Y坐标
* @param cChar :要显示的英文字符
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DispChar_EN ( uint16_t usX, uint16_t usY, const char cChar )
{
uint8_t byteCount, bitCount,fontLength;
uint16_t ucRelativePositon;
uint8_t *Pfont;
//对ascii码表偏移(字模表不包含ASCII表的前32个非图形符号)
ucRelativePositon = cChar - ' ';
//每个字模的字节数
fontLength = (LCD_Currentfonts->Width*LCD_Currentfonts->Height)/8;
//字模首地址
/*ascii码表偏移值乘以每个字模的字节数,求出字模的偏移位置*/
Pfont = (uint8_t *)&LCD_Currentfonts->table[ucRelativePositon * fontLength];
//设置显示窗口
ILI9341_OpenWindow ( usX, usY, LCD_Currentfonts->Width, LCD_Currentfonts->Height);
ILI9341_Write_Cmd ( CMD_SetPixel );
//按字节读取字模数据
//由于前面直接设置了显示窗口,显示数据会自动换行
for ( byteCount = 0; byteCount < fontLength; byteCount++ )
{
//一位一位处理要显示的颜色
for ( bitCount = 0; bitCount < 8; bitCount++ )
{
if ( Pfont[byteCount] & (0x80>>bitCount) )
ILI9341_Write_Data ( CurrentTextColor );
else
ILI9341_Write_Data ( CurrentBackColor );
}
}
}
/**
* @brief 在 ILI9341 显示器上显示一个中文字符
* @param usX :在特定扫描方向下字符的起始X坐标
* @param usY :在特定扫描方向下字符的起始Y坐标
* @param usChar :要显示的中文字符(国标码)
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DispChar_CH ( uint16_t usX, uint16_t usY, uint16_t usChar )
{
uint8_t rowCount, bitCount;
uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ];
uint16_t usTemp;
//设置显示窗口
ILI9341_OpenWindow ( usX, usY, WIDTH_CH_CHAR, HEIGHT_CH_CHAR );
ILI9341_Write_Cmd ( CMD_SetPixel );
//取字模数据
GetGBKCode ( ucBuffer, usChar );
for ( rowCount = 0; rowCount < HEIGHT_CH_CHAR; rowCount++ )
{
/* 取出两个字节的数据,在lcd上即是一个汉字的一行 */
usTemp = ucBuffer [ rowCount * 2 ];
usTemp = ( usTemp << 8 );
usTemp |= ucBuffer [ rowCount * 2 + 1 ];
for ( bitCount = 0; bitCount < WIDTH_CH_CHAR; bitCount ++ )
{
if ( usTemp & ( 0x8000 >> bitCount ) ) //高位在前
ILI9341_Write_Data ( CurrentTextColor );
else
ILI9341_Write_Data ( CurrentBackColor );
}
}
}
/**
* @brief 在 ILI9341 显示器上显示英文字符串
* @param line :在特定扫描方向下字符串的起始Y坐标
* 本参数可使用宏LINE(0)、LINE(1)等方式指定文字坐标,
* 宏LINE(x)会根据当前选择的字体来计算Y坐标值。
* 显示中文且使用LINE宏时,需要把英文字体设置成Font8x16
* @param pStr :要显示的英文字符串的首地址
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DispStringLine_EN ( uint16_t line, char * pStr )
{
uint16_t usX = 0;
while ( * pStr != '\0' )
{
if ( ( usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
line += LCD_Currentfonts->Height;
}
if ( ( line - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
line = ILI9341_DispWindow_Y_Star;
}
ILI9341_DispChar_EN ( usX, line, * pStr);
pStr ++;
usX += LCD_Currentfonts->Width;
}
}
/**
* @brief 在 ILI9341 显示器上显示英文字符串
* @param usX :在特定扫描方向下字符的起始X坐标
* @param usY :在特定扫描方向下字符的起始Y坐标
* @param pStr :要显示的英文字符串的首地址
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DispString_EN ( uint16_t usX ,uint16_t usY, char * pStr )
{
while ( * pStr != '\0' )
{
if ( ( usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
usY += LCD_Currentfonts->Height;
}
if ( ( usY - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
usY = ILI9341_DispWindow_Y_Star;
}
ILI9341_DispChar_EN ( usX, usY, * pStr);
pStr ++;
usX += LCD_Currentfonts->Width;
}
}
/**
* @brief 在 ILI9341 显示器上显示英文字符串(沿Y轴方向)
* @param usX :在特定扫描方向下字符的起始X坐标
* @param usY :在特定扫描方向下字符的起始Y坐标
* @param pStr :要显示的英文字符串的首地址
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DispString_EN_YDir ( uint16_t usX,uint16_t usY , char * pStr )
{
while ( * pStr != '\0' )
{
if ( ( usY - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height ) >LCD_Y_LENGTH )
{
usY = ILI9341_DispWindow_Y_Star;
usX += LCD_Currentfonts->Width;
}
if ( ( usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH)
{
usX = ILI9341_DispWindow_X_Star;
usY = ILI9341_DispWindow_Y_Star;
}
ILI9341_DispChar_EN ( usX, usY, * pStr);
pStr ++;
usY += LCD_Currentfonts->Height;
}
}
/**
* @brief 在 ILI9341 显示器上显示中英文字符串
* @param usX :在特定扫描方向下字符的起始X坐标
* @param usY :在特定扫描方向下字符的起始Y坐标
* @param pStr :要显示的字符串的首地址
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DispString_EN_CH ( uint16_t usX , uint16_t usY, char * pStr )
{
uint16_t usCh;
while( * pStr != '\0' )
{
if ( * pStr <= 126 ) //英文字符
{
if ( ( usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
usY += LCD_Currentfonts->Height;
}
if ( ( usY - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
usY = ILI9341_DispWindow_Y_Star;
}
ILI9341_DispChar_EN ( usX, usY, * pStr );
usX += LCD_Currentfonts->Width;
pStr ++;
}
else //汉字字符
{
if ( ( usX - ILI9341_DispWindow_X_Star + WIDTH_CH_CHAR ) > LCD_X_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
usY += HEIGHT_CH_CHAR;
}
if ( ( usY - ILI9341_DispWindow_Y_Star + HEIGHT_CH_CHAR ) > LCD_Y_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
usY = ILI9341_DispWindow_Y_Star;
}
usCh = * ( uint16_t * ) pStr;
usCh = ( usCh << 8 ) + ( usCh >> 8 );
ILI9341_DispChar_CH ( usX, usY, usCh );
usX += WIDTH_CH_CHAR;
pStr += 2; //一个汉字两个字节
}
}
}
/**
* @brief 在 ILI9341 显示器上显示中英文字符串
* @param line :在特定扫描方向下字符串的起始Y坐标
* 本参数可使用宏LINE(0)、LINE(1)等方式指定文字坐标,
* 宏LINE(x)会根据当前选择的字体来计算Y坐标值。
* 显示中文且使用LINE宏时,需要把英文字体设置成Font8x16
* @param pStr :要显示的字符串的首地址
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9341_DispStringLine_EN_CH ( uint16_t line, char * pStr )
{
uint16_t usCh;
uint16_t usX = 0;
while( * pStr != '\0' )
{
if ( * pStr <= 126 ) //英文字符
{
if ( ( usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
line += LCD_Currentfonts->Height;
}
if ( ( line - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
line = ILI9341_DispWindow_Y_Star;
}
ILI9341_DispChar_EN ( usX, line, * pStr );
usX += LCD_Currentfonts->Width;
pStr ++;
}
else //汉字字符
{
if ( ( usX - ILI9341_DispWindow_X_Star + WIDTH_CH_CHAR ) > LCD_X_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
line += HEIGHT_CH_CHAR;
}
if ( ( line - ILI9341_DispWindow_Y_Star + HEIGHT_CH_CHAR ) > LCD_Y_LENGTH )
{
usX = ILI9341_DispWindow_X_Star;
line = ILI9341_DispWindow_Y_Star;
}
usCh = * ( uint16_t * ) pStr;
usCh = ( usCh << 8 ) + ( usCh >> 8 );
ILI9341_DispChar_CH ( usX, line, usCh );
usX += WIDTH_CH_CHAR;
pStr += 2; //一个汉字两个字节
}
}
}
/**
* @brief 设置英文字体类型
* @param fonts: 指定要选择的字体
* 参数为以下值之一
* @arg:Font24x32;
* @arg:Font16x24;
* @arg:Font8x16;
* @retval None
*/
void LCD_SetFont(sFONT *fonts)
{
LCD_Currentfonts = fonts;
}
/**
* @brief 获取当前字体类型
* @param None.
* @retval 返回当前字体类型
*/
sFONT *LCD_GetFont(void)
{
return LCD_Currentfonts;
}
/**
* @brief 设置LCD的前景(字体)及背景颜色,RGB565
* @param TextColor: 指定前景(字体)颜色
* @param BackColor: 指定背景颜色
* @retval None
*/
void LCD_SetColors(uint16_t TextColor, uint16_t BackColor)
{
CurrentTextColor = TextColor;
CurrentBackColor = BackColor;
}
/**
* @brief 获取LCD的前景(字体)及背景颜色,RGB565
* @param TextColor: 用来存储前景(字体)颜色的指针变量
* @param BackColor: 用来存储背景颜色的指针变量
* @retval None
*/
void LCD_GetColors(uint16_t *TextColor, uint16_t *BackColor)
{
*TextColor = CurrentTextColor;
*BackColor = CurrentBackColor;
}
/**
* @brief 设置LCD的前景(字体)颜色,RGB565
* @param Color: 指定前景(字体)颜色
* @retval None
*/
void LCD_SetTextColor(uint16_t Color)
{
CurrentTextColor = Color;
}
/**
* @brief 设置LCD的背景颜色,RGB565
* @param Color: 指定背景颜色
* @retval None
*/
void LCD_SetBackColor(uint16_t Color)
{
CurrentBackColor = Color;
}
/**
* @brief 清除某行文字
* @param Line: 指定要删除的行
* 本参数可使用宏LINE(0)、LINE(1)等方式指定要删除的行,
* 宏LINE(x)会根据当前选择的字体来计算Y坐标值,并删除当前字体高度的第x行。
* @retval None
*/
void LCD_ClearLine(uint16_t Line)
{
ILI9341_Clear(0,Line,LCD_X_LENGTH,((sFONT *)LCD_GetFont())->Height); /* 清屏,显示全黑 */
}
/***********************缩放字体****************************/
#define ZOOMMAXBUFF 16384
uint8_t zoomBuff[ZOOMMAXBUFF] = {0}; //用于缩放的缓存,最大支持到128*128
uint8_t zoomTempBuff[1024] = {0};
/**
* @brief 缩放字模,缩放后的字模由1个像素点由8个数据位来表示
0x01表示笔迹,0x00表示空白区
* @param in_width :原始字符宽度
* @param in_heig :原始字符高度
* @param out_width :缩放后的字符宽度
* @param out_heig:缩放后的字符高度
* @param in_ptr :字库输入指针 注意:1pixel 1bit
* @param out_ptr :缩放后的字符输出指针 注意: 1pixel 8bit
* out_ptr实际上没有正常输出,改成了直接输出到全局指针zoomBuff中
* @param en_cn :0为英文,1为中文
* @retval 无
*/
void ILI9341_zoomChar(uint16_t in_width, //原始字符宽度
uint16_t in_heig, //原始字符高度
uint16_t out_width, //缩放后的字符宽度
uint16_t out_heig, //缩放后的字符高度
uint8_t *in_ptr, //字库输入指针 注意:1pixel 1bit
uint8_t *out_ptr, //缩放后的字符输出指针 注意: 1pixel 8bit
uint8_t en_cn) //0为英文,1为中文
{
uint8_t *pts,*ots;
//根据源字模及目标字模大小,设定运算比例因子,左移16是为了把浮点运算转成定点运算
unsigned int xrIntFloat_16=(in_width<<16)/out_width+1;
unsigned int yrIntFloat_16=(in_heig<<16)/out_heig+1;
unsigned int srcy_16=0;
unsigned int y,x;
uint8_t *pSrcLine;
uint16_t byteCount,bitCount;
//检查参数是否合法
if(in_width >= 32) return; //字库不允许超过32像素
if(in_width * in_heig == 0) return;
if(in_width * in_heig >= 1024 ) return; //限制输入最大 32*32
if(out_width * out_heig == 0) return;
if(out_width * out_heig >= ZOOMMAXBUFF ) return; //限制最大缩放 128*128
pts = (uint8_t*)&zoomTempBuff;
//为方便运算,字库的数据由1 pixel/1bit 映射到1pixel/8bit
//0x01表示笔迹,0x00表示空白区
if(en_cn == 0x00)//英文
{
//英文和中文字库上下边界不对,可在此处调整。需要注意tempBuff防止溢出
for(byteCount=0;byteCount<in_heig*in_width/8;byteCount++)
{
for(bitCount=0;bitCount<8;bitCount++)
{
//把源字模数据由位映射到字节
//in_ptr里bitX为1,则pts里整个字节值为1
//in_ptr里bitX为0,则pts里整个字节值为0
*pts++ = (in_ptr[byteCount] & (0x80>>bitCount))?1:0;
}
}
}
else //中文
{
for(byteCount=0;byteCount<in_heig*in_width/8;byteCount++)
{
for(bitCount=0;bitCount<8;bitCount++)
{
//把源字模数据由位映射到字节
//in_ptr里bitX为1,则pts里整个字节值为1
//in_ptr里bitX为0,则pts里整个字节值为0
*pts++ = (in_ptr[byteCount] & (0x80>>bitCount))?1:0;
}
}
}
//zoom过程
pts = (uint8_t*)&zoomTempBuff; //映射后的源数据指针
ots = (uint8_t*)&zoomBuff; //输出数据的指针
for (y=0;y<out_heig;y++) /*行遍历*/
{
unsigned int srcx_16=0;
pSrcLine=pts+in_width*(srcy_16>>16);
for (x=0;x<out_width;x++) /*行内像素遍历*/
{
ots[x]=pSrcLine[srcx_16>>16]; //把源字模数据复制到目标指针中
srcx_16+=xrIntFloat_16; //按比例偏移源像素点
}
srcy_16+=yrIntFloat_16; //按比例偏移源像素点
ots+=out_width;
}
/*!!!缩放后的字模数据直接存储到全局指针zoomBuff里了*/
out_ptr = (uint8_t*)&zoomBuff; //out_ptr没有正确传出,后面调用直接改成了全局变量指针!
/*实际中如果使用out_ptr不需要下面这一句!!!
只是因为out_ptr没有使用,会导致warning。强迫症*/
out_ptr++;
}
/**
* @brief 利用缩放后的字模显示字符
* @param Xpos :字符显示位置x
* @param Ypos :字符显示位置y
* @param Font_width :字符宽度
* @param Font_Heig:字符高度
* @param c :要显示的字模数据
* @param DrawModel :是否反色显示
* @retval 无
*/
void ILI9341_DrawChar_Ex(uint16_t usX, //字符显示位置x
uint16_t usY, //字符显示位置y
uint16_t Font_width, //字符宽度
uint16_t Font_Height, //字符高度
uint8_t *c, //字模数据
uint16_t DrawModel) //是否反色显示
{
uint32_t index = 0, counter = 0;
//设置显示窗口
ILI9341_OpenWindow ( usX, usY, Font_width, Font_Height);
ILI9341_Write_Cmd ( CMD_SetPixel );
//按字节读取字模数据
//由于前面直接设置了显示窗口,显示数据会自动换行
for ( index = 0; index < Font_Height; index++ )
{
//一位一位处理要显示的颜色
for ( counter = 0; counter < Font_width; counter++ )
{
//缩放后的字模数据,以一个字节表示一个像素位
//整个字节值为1表示该像素为笔迹
//整个字节值为0表示该像素为背景
if ( *c++ == DrawModel )
ILI9341_Write_Data ( CurrentBackColor );
else
ILI9341_Write_Data ( CurrentTextColor );
}
}
}
/**
* @brief 利用缩放后的字模显示字符串
* @param Xpos :字符显示位置x
* @param Ypos :字符显示位置y
* @param Font_width :字符宽度,英文字符在此基础上/2。注意为偶数
* @param Font_Heig:字符高度,注意为偶数
* @param c :要显示的字符串
* @param DrawModel :是否反色显示
* @retval 无
*/
void ILI9341_DisplayStringEx(uint16_t x, //字符显示位置x
uint16_t y, //字符显示位置y
uint16_t Font_width, //要显示的字体宽度,英文字符在此基础上/2。注意为偶数
uint16_t Font_Height, //要显示的字体高度,注意为偶数
uint8_t *ptr, //显示的字符内容
uint16_t DrawModel) //是否反色显示
{
uint16_t Charwidth = Font_width; //默认为Font_width,英文宽度为中文宽度的一半
uint8_t *psr;
uint8_t Ascii; //英文
uint16_t usCh; //中文
uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ];
while ( *ptr != '\0')
{
/****处理换行*****/
if ( ( x - ILI9341_DispWindow_X_Star + Charwidth ) > LCD_X_LENGTH )
{
x = ILI9341_DispWindow_X_Star;
y += Font_Height;
}
if ( ( y - ILI9341_DispWindow_Y_Star + Font_Height ) > LCD_Y_LENGTH )
{
x = ILI9341_DispWindow_X_Star;
y = ILI9341_DispWindow_Y_Star;
}
if(*ptr > 0x80) //如果是中文
{
Charwidth = Font_width;
usCh = * ( uint16_t * ) ptr;
usCh = ( usCh << 8 ) + ( usCh >> 8 );
GetGBKCode ( ucBuffer, usCh ); //取字模数据
//缩放字模数据,源字模为16*16
ILI9341_zoomChar(WIDTH_CH_CHAR,HEIGHT_CH_CHAR,Charwidth,Font_Height,(uint8_t *)&ucBuffer,psr,1);
//显示单个字符
ILI9341_DrawChar_Ex(x,y,Charwidth,Font_Height,(uint8_t*)&zoomBuff,DrawModel);
x+=Charwidth;
ptr+=2;
}
else
{
Charwidth = Font_width / 2;
Ascii = *ptr - 32;
//使用16*24字体缩放字模数据
ILI9341_zoomChar(16,24,Charwidth,Font_Height,(uint8_t *)&Font16x24.table[Ascii * Font16x24.Height*Font16x24.Width/8],psr,0);
//显示单个字符
ILI9341_DrawChar_Ex(x,y,Charwidth,Font_Height,(uint8_t*)&zoomBuff,DrawModel);
x+=Charwidth;
ptr++;
}
}
}
/**
* @brief 利用缩放后的字模显示字符串(沿Y轴方向)
* @param Xpos :字符显示位置x
* @param Ypos :字符显示位置y
* @param Font_width :字符宽度,英文字符在此基础上/2。注意为偶数
* @param Font_Heig:字符高度,注意为偶数
* @param c :要显示的字符串
* @param DrawModel :是否反色显示
* @retval 无
*/
void ILI9341_DisplayStringEx_YDir(uint16_t x, //字符显示位置x
uint16_t y, //字符显示位置y
uint16_t Font_width, //要显示的字体宽度,英文字符在此基础上/2。注意为偶数
uint16_t Font_Height, //要显示的字体高度,注意为偶数
uint8_t *ptr, //显示的字符内容
uint16_t DrawModel) //是否反色显示
{
uint16_t Charwidth = Font_width; //默认为Font_width,英文宽度为中文宽度的一半
uint8_t *psr;
uint8_t Ascii; //英文
uint16_t usCh; //中文
uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ];
while ( *ptr != '\0')
{
//统一使用汉字的宽高来计算换行
if ( ( y - ILI9341_DispWindow_X_Star + Font_width ) > LCD_Y_LENGTH )
{
y = ILI9341_DispWindow_X_Star;
x += Font_width;
}
if ( ( x - ILI9341_DispWindow_Y_Star + Font_Height ) > LCD_X_LENGTH )
{
y = ILI9341_DispWindow_X_Star;
x = ILI9341_DispWindow_Y_Star;
}
if(*ptr > 0x80) //如果是中文
{
Charwidth = Font_width;
usCh = * ( uint16_t * ) ptr;
usCh = ( usCh << 8 ) + ( usCh >> 8 );
GetGBKCode ( ucBuffer, usCh ); //取字模数据
//缩放字模数据,源字模为16*16
ILI9341_zoomChar(WIDTH_CH_CHAR,HEIGHT_CH_CHAR,Charwidth,Font_Height,(uint8_t *)&ucBuffer,psr,1);
//显示单个字符
ILI9341_DrawChar_Ex(x,y,Charwidth,Font_Height,(uint8_t*)&zoomBuff,DrawModel);
y+=Font_Height;
ptr+=2;
}
else
{
Charwidth = Font_width / 2;
Ascii = *ptr - 32;
//使用16*24字体缩放字模数据
ILI9341_zoomChar(16,24,Charwidth,Font_Height,(uint8_t *)&Font16x24.table[Ascii * Font16x24.Height*Font16x24.Width/8],psr,0);
//显示单个字符
ILI9341_DrawChar_Ex(x,y,Charwidth,Font_Height,(uint8_t*)&zoomBuff,DrawModel);
y+=Font_Height;
ptr++;
}
}
}
/*********************end of file*************************/
LD3320.c/usart.c
#include "bsp_usart.h"
#include <string.h>
/**
* @brief USART GPIO 配置,工作参数配置
* @param 无
* @retval 无
*/
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
/**
* @brief 配置嵌套向量中断控制器NVIC
* @param 无
* @retval 无
*/
static void NVIC_USART3_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;//使能中断接收
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
/*
PB10 :TXD
PB11 : RXD
*/
//串口IO初始化函数
void USART3_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure; //IO端口的初始化
USART_InitTypeDef USART_InitStructure; //串口的初始化
RCC_APB2PeriphClockCmd(Ld3320_USART_GPIO_CLK, ENABLE); //使能IO端口的时钟
RCC_APB1PeriphClockCmd(Ld3320_USART_CLK, ENABLE); //使能串口的时钟
//发送
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = Ld3320_USART_TX_GPIO_PIN; //发送引脚
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//接收
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = Ld3320_USART_RX_GPIO_PIN; //接收引脚
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = baudrate; //设置传输的波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置传输一帧数据的数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一位停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //能使接收的发送
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控制
USART_Init(Ld3320_USARTx,&USART_InitStructure);
NVIC_USART3_Configuration();
USART_ITConfig(Ld3320_USARTx,USART_IT_RXNE,ENABLE); //使能串口中断
USART_Cmd(Ld3320_USARTx,ENABLE); //使能串口2
}
/*
函数名:USART3串口发送函数
功能: 发送数据
入口参数:发送的字符
*/
void USART3_SendString(u8 *str)
{
u8 index=0;
do
{
USART_SendData(USART3,str[index]); //逐一的发送数组中的内容
while(USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET); //判断是否发送完 发完为高电平
index++;
}
while(str[index] != 0); //检查字符串结束标志
}
xUSATR_TypeDef xUSART; // 声明为全局变量,方便记录信息、状态
// USART-1 //
/
/******************************************************************************
* 函 数: vUSART1_Init
* 功 能: 初始化USART1的GPIO、通信参数配置、中断优先级
* (8位数据、无校验、1个停止位)
* 参 数: uint32_t baudrate 通信波特率
* 返回值: 无
******************************************************************************/
void USART1_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 时钟使能
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 使能USART1时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能GPIOA时钟
// GPIO_TX引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // TX引脚工作模式:复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure);
// GPIO_RX引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // RX引脚工作模式:上拉输入; 如果使用浮空输入,引脚空置时可能产生误输入; 当电路上为一主多从电路时,可以使用复用开漏模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 中断配置
NVIC_InitStructure .NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority = 2 ; // 抢占优先级
NVIC_InitStructure .NVIC_IRQChannelSubPriority = 2; // 子优先级
NVIC_InitStructure .NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
//USART 初始化设置
USART_DeInit(USART1);
USART_InitStructure.USART_BaudRate = baudrate; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 使能收、发模式
USART_Init(USART1, &USART_InitStructure); // 初始化串口
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能接受中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 使能空闲中断
USART_Cmd(USART1, ENABLE); // 使能串口, 开始工作
USART1->SR = ~(0x00F0); // 清理中断
xUSART.USART1InitFlag = 1; // 标记初始化标志
xUSART.USART1ReceivedNum = 0; // 接收字节数清零
printf("\r\r\r=========== 魔女开发板 STM32F103 外设初始报告 ===========\r");
printf("USART1初始化配置 接收中断、空闲中断, 发送中断\r");
}
/******************************************************************************
* 函 数: USART1_IRQHandler
* 功 能: USART1的接收中断、空闲中断、发送中断
* 参 数: 无
* 返回值: 无
*
******************************************************************************/
static uint8_t U1TxBuffer[256] ; // 用于中断发送:环形缓冲区,256个字节
static uint8_t U1TxCounter = 0 ; // 用于中断发送:标记已发送的字节数(环形)
static uint8_t U1TxCount = 0 ; // 用于中断发送:标记将要发送的字节数(环形)
void USART1_IRQHandler(void)
{
static uint16_t cnt = 0; // 接收字节数累计:每一帧数据已接收到的字节数
static uint8_t RxTemp[U1_RX_BUF_SIZE]; // 接收数据缓存数组:每新接收1个字节,先顺序存放到这里,当一帧接收完(发生空闲中断), 再转存到全局变量:xUSART.USARTxReceivedBuffer[xx]中;
// 接收中断
if (USART1->SR & (1 << 5)) // 检查RXNE(读数据寄存器非空标志位); RXNE中断清理方法:读DR时自动清理;
{
if ((cnt >= U1_RX_BUF_SIZE))//||(xUSART.USART1ReceivedFlag==1// 判断1: 当前帧已接收到的数据量,已满(缓存区), 为避免溢出,本包后面接收到的数据直接舍弃.
{
// 判断2: 如果之前接收好的数据包还没处理,就放弃新数据,即,新数据帧不能覆盖旧数据帧,直至旧数据帧被处理.缺点:数据传输过快于处理速度时会掉包;好处:机制清晰,易于调试
USART1->DR; // 读取数据寄存器的数据,但不保存.主要作用:读DR时自动清理接收中断标志;
return;
}
RxTemp[cnt++] = USART1->DR ; // 把新收到的字节数据,顺序存放到RXTemp数组中;注意:读取DR时自动清零中断位;
}
// 空闲中断, 用于配合接收中断,以判断一帧数据的接收完成
if (USART1->SR & (1 << 4)) // 检查IDLE(空闲中断标志位); IDLE中断标志清理方法:序列清零,USART1 ->SR; USART1 ->DR;
{
xUSART.USART1ReceivedNum = 0; // 把接收到的数据字节数清0
memcpy(xUSART.USART1ReceivedBuffer, RxTemp, U1_RX_BUF_SIZE); // 把本帧接收到的数据,存放到全局变量xUSART.USARTxReceivedBuffer中, 等待处理; 注意:复制的是整个数组,包括0值,以方便字符串数据
xUSART.USART1ReceivedNum = cnt; // 把接收到的字节数,存放到全局变量xUSART.USARTxReceivedCNT中;
cnt = 0; // 接收字节数累计器,清零; 准备下一次的接收
memset(RxTemp, 0, U1_RX_BUF_SIZE); // 接收数据缓存数组,清零; 准备下一次的接收
USART1 ->SR;
USART1 ->DR; // 清零IDLE中断标志位!! 序列清零,顺序不能错!!
}
// 发送中断
if ((USART1->SR & 1 << 7) && (USART1->CR1 & 1 << 7)) // 检查TXE(发送数据寄存器空)、TXEIE(发送缓冲区空中断使能)
{
USART1->DR = U1TxBuffer[U1TxCounter++]; // 读取数据寄存器值;注意:读取DR时自动清零中断位;
if (U1TxCounter == U1TxCount)
USART1->CR1 &= ~(1 << 7); // 已发送完成,关闭发送缓冲区空置中断 TXEIE
}
}
/******************************************************************************
* 函 数: vUSART1_GetBuffer
* 功 能: 获取UART所接收到的数据
* 参 数: uint8_t* buffer 数据存放缓存地址
* uint8_t* cnt 接收到的字节数
* 返回值: 0_没有接收到新数据, 非0_所接收到新数据的字节数
******************************************************************************/
uint8_t USART1_GetBuffer(uint8_t *buffer, uint8_t *cnt)
{
if (xUSART.USART1ReceivedNum > 0) // 判断是否有新数据
{
memcpy(buffer, xUSART.USART1ReceivedBuffer, xUSART.USART1ReceivedNum); // 把新数据复制到指定位置
*cnt = xUSART.USART1ReceivedNum; // 把新数据的字节数,存放指定变量
xUSART.USART1ReceivedNum = 0; // 接收标记置0
return *cnt; // 返回所接收到新数据的字节数
}
return 0; // 返回0, 表示没有接收到新数据
}
/******************************************************************************
* 函 数: vUSART1_SendData
* 功 能: UART通过中断发送数据,适合各种数据类型
* 【适合场景】本函数可发送各种数据,而不限于字符串,如int,char
* 【不 适 合】注意环形缓冲区容量256字节,如果发送频率太高,注意波特率
* 参 数: uint8_t* buffer 需发送数据的首地址
* uint8_t cnt 发送的字节数 ,限于中断发送的缓存区大小,不能大于256个字节
* 返回值:
******************************************************************************/
void USART1_SendData(uint8_t *buf, uint8_t cnt)
{
for (uint8_t i = 0; i < cnt; i++)
U1TxBuffer[U1TxCount++] = buf[i];
if ((USART1->CR1 & 1 << 7) == 0) // 检查发送缓冲区空置中断(TXEIE)是否已打开
USART1->CR1 |= 1 << 7;
}
/******************************************************************************
* 函 数: vUSART1_SendString
* 功 能: UART通过中断发送输出字符串,无需输入数据长度
* 【适合场景】字符串,长度<=256字节
* 【不 适 合】int,float等数据类型
* 参 数: char* stringTemp 需发送数据的缓存首地址
* 返回值: 元
******************************************************************************/
void USART1_SendString(char *stringTemp)
{
u16 num = 0; // 字符串长度
char *t = stringTemp ; // 用于配合计算发送的数量
while (*t++ != 0) num++; // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位
USART1_SendData((u8 *)stringTemp, num); // 注意调用函数所需要的真实数据长度; 如果目标需要以0作结尾判断,需num+1:字符串以0结尾,即多发一个:0
}
/******************************************************************************
* 函 数: vUSART1_SendStringForDMA
* 功 能: UART通过DMA发送数据,省了占用中断的时间
* 【适合场景】字符串,字节数非常多,
* 【不 适 合】1:只适合发送字符串,不适合发送可能含0的数值类数据; 2-时间间隔要足够
* 参 数: char strintTemp 要发送的字符串首地址
* 返回值: 无
******************************************************************************/
void USART1_SendStringForDMA(char *stringTemp)
{
static u8 Flag_DmaTxInit = 0; // 用于标记是否已配置DMA发送
u32 num = 0; // 发送的数量,注意发送的单位不是必须8位的
char *t = stringTemp ; // 用于配合计算发送的数量
while (*t++ != 0) num++; // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位
while (DMA1_Channel4->CNDTR > 0); // 重要:如果DMA还在进行上次发送,就等待; 得进完成中断清标志,F4不用这么麻烦,发送完后EN自动清零
if (Flag_DmaTxInit == 0) // 是否已进行过USAART_TX的DMA传输配置
{
Flag_DmaTxInit = 1; // 设置标记,下次调用本函数就不再进行配置了
USART1 ->CR3 |= 1 << 7; // 使能DMA发送
RCC->AHBENR |= 1 << 0; // 开启DMA1时钟 [0]DMA1 [1]DMA2
DMA1_Channel4->CCR = 0; // 失能, 清0整个寄存器, DMA必须失能才能配置
DMA1_Channel4->CNDTR = num; // 传输数据量
DMA1_Channel4->CMAR = (u32)stringTemp; // 存储器地址
DMA1_Channel4->CPAR = (u32)&USART1->DR; // 外设地址
DMA1_Channel4->CCR |= 1 << 4; // 数据传输方向 0:从外设读 1:从存储器读
DMA1_Channel4->CCR |= 0 << 5; // 循环模式 0:不循环 1:循环
DMA1_Channel4->CCR |= 0 << 6; // 外设地址非增量模式
DMA1_Channel4->CCR |= 1 << 7; // 存储器增量模式
DMA1_Channel4->CCR |= 0 << 8; // 外设数据宽度为8位
DMA1_Channel4->CCR |= 0 << 10; // 存储器数据宽度8位
DMA1_Channel4->CCR |= 0 << 12; // 中等优先级
DMA1_Channel4->CCR |= 0 << 14; // 非存储器到存储器模式
}
DMA1_Channel4->CCR &= ~((u32)(1 << 0)); // 失能,DMA必须失能才能配置
DMA1_Channel4->CNDTR = num; // 传输数据量
DMA1_Channel4->CMAR = (u32)stringTemp; // 存储器地址
DMA1_Channel4->CCR |= 1 << 0; // 开启DMA传输
}
// USART-2 //
/
/******************************************************************************
* 函 数: vUSART2_Init
* 功 能: 初始化USART的GPIO、通信参数配置、中断优先级
* (8位数据、无校验、1个停止位)
* 参 数: uint32_t baudrate 通信波特率
* 返回值: 无
******************************************************************************/
void USART2_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 时钟使能
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; // 使能USART2时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能GPIOA时钟
// GPIO_TX引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // TX引脚工作模式:复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure);
// GPIO_RX引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // RX引脚工作模式:上拉输入; 如果使用浮空输入,引脚空置时可能产生误输入; 当电路上为一主多从电路时,可以使用复用开漏模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 中断配置
NVIC_InitStructure .NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority = 2 ; // 抢占优先级
NVIC_InitStructure .NVIC_IRQChannelSubPriority = 2; // 子优先级
NVIC_InitStructure .NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
//USART 初始化设置
//USART_DeInit(USART2);
USART_InitStructure.USART_BaudRate = baudrate; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 使能收、发模式
USART_Init(USART2, &USART_InitStructure); // 初始化串口
USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); // 使能接受中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); // 使能空闲中断
USART_Cmd(USART2, ENABLE); // 使能串口, 开始工作
USART2->SR = ~(0x00F0); // 清理中断
xUSART.USART2InitFlag = 1; // 标记初始化标志
xUSART.USART2ReceivedNum = 0; // 接收字节数清零
printf("\rUSART2初始化配置 接收中断、空闲中断, 发送中断\r");
}
/******************************************************************************
* 函 数: USART2_IRQHandler
* 功 能: USART2的接收中断、空闲中断、发送中断
* 参 数: 无
* 返回值: 无
******************************************************************************/
static uint8_t U2TxBuffer[256] ; // 用于中断发送:环形缓冲区,256个字节
static uint8_t U2TxCounter = 0 ; // 用于中断发送:标记已发送的字节数(环形)
static uint8_t U2TxCount = 0 ; // 用于中断发送:标记将要发送的字节数(环形)
void USART2_IRQHandler(void)
{
static uint16_t cnt = 0; // 接收字节数累计:每一帧数据已接收到的字节数
static uint8_t RxTemp[U2_RX_BUF_SIZE]; // 接收数据缓存数组:每新接收1个字节,先顺序存放到这里,当一帧接收完(发生空闲中断), 再转存到全局变量:xUSART.USARTxReceivedBuffer[xx]中;
// 接收中断
if (USART2->SR & (1 << 5)) // 检查RXNE(读数据寄存器非空标志位); RXNE中断清理方法:读DR时自动清理;
{
if ((cnt >= U2_RX_BUF_SIZE))//||xUSART.USART2ReceivedFlag==1 // 判断1: 当前帧已接收到的数据量,已满(缓存区), 为避免溢出,本包后面接收到的数据直接舍弃.
{
// 判断2: 如果之前接收好的数据包还没处理,就放弃新数据,即,新数据帧不能覆盖旧数据帧,直至旧数据帧被处理.缺点:数据传输过快于处理速度时会掉包;好处:机制清晰,易于调试
USART2->DR; // 读取数据寄存器的数据,但不保存.主要作用:读DR时自动清理接收中断标志;
return;
}
RxTemp[cnt++] = USART2->DR ; // 把新收到的字节数据,顺序存放到RXTemp数组中;注意:读取DR时自动清零中断位;
}
// 空闲中断, 用于配合接收中断,以判断一帧数据的接收完成
if (USART2->SR & (1 << 4)) // 检查IDLE(空闲中断标志位); IDLE中断标志清理方法:序列清零,USART1 ->SR; USART1 ->DR;
{
xUSART.USART2ReceivedNum = 0; // 把接收到的数据字节数清0
memcpy(xUSART.USART2ReceivedBuffer, RxTemp, U2_RX_BUF_SIZE); // 把本帧接收到的数据,存放到全局变量xUSART.USARTxReceivedBuffer中, 等待处理; 注意:复制的是整个数组,包括0值,以方便字符串数据
xUSART.USART2ReceivedNum = cnt; // 把接收到的字节数,存放到全局变量xUSART.USARTxReceivedCNT中;
cnt = 0; // 接收字节数累计器,清零; 准备下一次的接收
memset(RxTemp, 0, U2_RX_BUF_SIZE); // 接收数据缓存数组,清零; 准备下一次的接收
USART2 ->SR;
USART2 ->DR; // 清零IDLE中断标志位!! 序列清零,顺序不能错!!
}
// 发送中断
if ((USART2->SR & 1 << 7) && (USART2->CR1 & 1 << 7)) // 检查TXE(发送数据寄存器空)、TXEIE(发送缓冲区空中断使能)
{
USART2->DR = U2TxBuffer[U2TxCounter++]; // 读取数据寄存器值;注意:读取DR时自动清零中断位;
if (U2TxCounter == U2TxCount)
USART2->CR1 &= ~(1 << 7); // 已发送完成,关闭发送缓冲区空置中断 TXEIE
}
}
/******************************************************************************
* 函 数: vUSART2_GetBuffer
* 功 能: 获取UART所接收到的数据
* 参 数: uint8_t* buffer 数据存放缓存地址
* uint8_t* cnt 接收到的字节数
* 返回值: 0_没有接收到新数据, 非0_所接收到新数据的字节数
******************************************************************************/
uint8_t USART2_GetBuffer(uint8_t *buffer, uint8_t *cnt)
{
if (xUSART.USART2ReceivedNum > 0) // 判断是否有新数据
{
memcpy(buffer, xUSART.USART2ReceivedBuffer, xUSART.USART2ReceivedNum); // 把新数据复制到指定位置
*cnt = xUSART.USART2ReceivedNum; // 把新数据的字节数,存放指定变量
xUSART.USART2ReceivedNum = 0; // 接收标记置0
return *cnt; // 返回所接收到新数据的字节数
}
return 0; // 返回0, 表示没有接收到新数据
}
/******************************************************************************
* 函 数: vUSART2_SendData
* 功 能: UART通过中断发送数据,适合各种数据类型
* 【适合场景】本函数可发送各种数据,而不限于字符串,如int,char
* 【不 适 合】注意环形缓冲区容量256字节,如果发送频率太高,注意波特率
* 参 数: uint8_t* buffer 需发送数据的首地址
* uint8_t cnt 发送的字节数 ,限于中断发送的缓存区大小,不能大于256个字节
* 返回值:
******************************************************************************/
void USART2_SendData(uint8_t *buf, uint8_t cnt)
{
for (uint8_t i = 0; i < cnt; i++)
U2TxBuffer[U2TxCount++] = buf[i];
if ((USART2->CR1 & 1 << 7) == 0) // 检查发送缓冲区空置中断(TXEIE)是否已打开
USART2->CR1 |= 1 << 7;
}
/******************************************************************************
* 函 数: vUSART2_SendString
* 功 能: UART通过中断发送输出字符串,无需输入数据长度
* 【适合场景】字符串,长度<=256字节
* 【不 适 合】int,float等数据类型
* 参 数: char* stringTemp 需发送数据的缓存首地址
* 返回值: 元
******************************************************************************/
void USART2_SendString(char *stringTemp)
{
u16 num = 0; // 字符串长度
char *t = stringTemp ; // 用于配合计算发送的数量
while (*t++ != 0) num++; // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位
USART2_SendData((u8 *)stringTemp, num); // 注意调用函数所需要的真实数据长度; 如果目标需要以0作结尾判断,需num+1:字符串以0结尾,即多发一个:0
}
/******************************************************************************
* 函 数: USART3_IRQHandler
* 功 能: USART的接收中断、空闲中断、发送中断
* 参 数: 无
* 返回值: 无
******************************************************************************/
static uint8_t U3TxBuffer[256] ; // 用于中断发送:环形缓冲区,256个字节
static uint8_t U3TxCounter = 0 ; // 用于中断发送:标记已发送的字节数(环形)
static uint8_t U3TxCount = 0 ; // 用于中断发送:标记将要发送的字节数(环形)
void USART3_SendData(uint8_t *buf, uint8_t cnt)
{
for (uint8_t i = 0; i < cnt; i++)
U3TxBuffer[U3TxCount++] = buf[i];
if ((USART3->CR1 & 1 << 7) == 0) // 检查发送缓冲区空置中断(TXEIE)是否已打开
USART3->CR1 |= 1 << 7;
}
/******************************************************************************
* 函 数: vUSART3_SendString
* 功 能: UART通过中断发送输出字符串,无需输入数据长度
* 【适合场景】字符串,长度<=256字节
* 【不 适 合】int,float等数据类型
* 参 数: char* stringTemp 需发送数据的缓存首地址
* 返回值: 元
******************************************************************************/
//void USART3_SendString(char *stringTemp)
//{
// u16 num = 0; // 字符串长度
// char *t = stringTemp ; // 用于配合计算发送的数量
// while (*t++ != 0) num++; // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位
// USART3_SendData((u8 *)stringTemp, num); // 注意调用函数所需要的真实数据长度; 如果目标需要以0作结尾判断,需num+1:字符串以0结尾,即多发一个:0
//}
#ifdef STM32F10X_HD // STM32F103R,及以上,才有UART4和UART5
// UART-4 //
/
/******************************************************************************
* 函 数: vUART4_Init
* 功 能: 初始化USART的GPIO、通信参数配置、中断优先级
* (8位数据、无校验、1个停止位)
* 参 数: uint32_t baudrate 通信波特率
* 返回值: 无
******************************************************************************/
void UART4_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 时钟使能
RCC->APB1ENR |= RCC_APB1ENR_UART4EN; // 使能UART4时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // 使能GPIOC时钟
// GPIO_TX引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // TX引脚工作模式:复用推挽 GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_Init(GPIOC, &GPIO_InitStructure);
// GPIO_RX引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // RX引脚工作模式:上拉输入; 如果使用浮空输入,引脚空置时可能产生误输入; 当电路上为一主多从电路时,可以使用复用开漏模式
GPIO_Init(GPIOC, &GPIO_InitStructure);
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 中断配置
NVIC_InitStructure .NVIC_IRQChannel = UART4_IRQn;
NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority = 2 ; // 抢占优先级
NVIC_InitStructure .NVIC_IRQChannelSubPriority = 2; // 子优先级
NVIC_InitStructure .NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
//USART 初始化设置
USART_DeInit(UART4);
USART_InitStructure.USART_BaudRate = baudrate; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 使能收、发模式
USART_Init(UART4, &USART_InitStructure); // 初始化串口
USART_ITConfig(UART4, USART_IT_TXE, DISABLE);
USART_ITConfig(UART4, USART_IT_RXNE, ENABLE); // 使能接受中断
USART_ITConfig(UART4, USART_IT_IDLE, ENABLE); // 使能空闲中断
USART_Cmd(UART4, ENABLE); // 使能串口, 开始工作
UART4->SR = ~(0x00F0); // 清理中断
xUSART.UART4InitFlag = 1; // 标记初始化标志
xUSART.UART4ReceivedNum = 0; // 接收字节数清零
printf("\rUART4 初始化配置 接收中断、空闲中断, 发送中断\r");
}
/******************************************************************************
* 函 数: UART4_IRQHandler
* 功 能: USART2的接收中断、空闲中断、发送中断
* 参 数: 无
* 返回值: 无
******************************************************************************/
static uint8_t U4TxBuffer[256] ; // 用于中断发送:环形缓冲区,256个字节
static uint8_t U4TxCounter = 0 ; // 用于中断发送:标记已发送的字节数(环形)
static uint8_t U4TxCount = 0 ; // 用于中断发送:标记将要发送的字节数(环形)
void UART4_IRQHandler(void)
{
static uint16_t cnt = 0; // 接收字节数累计:每一帧数据已接收到的字节数
static uint8_t RxTemp[U4_RX_BUF_SIZE]; // 接收数据缓存数组:每新接收1个字节,先顺序存放到这里,当一帧接收完(发生空闲中断), 再转存到全局变量:xUSART.USARTxReceivedBuffer[xx]中;
// 接收中断
if (UART4->SR & (1 << 5)) // 检查RXNE(读数据寄存器非空标志位); RXNE中断清理方法:读DR时自动清理;
{
if ((cnt >= U4_RX_BUF_SIZE))//||xUSART.UART4ReceivedFlag==1 // 判断1: 当前帧已接收到的数据量,已满(缓存区), 为避免溢出,本包后面接收到的数据直接舍弃.
{
// 判断2: 如果之前接收好的数据包还没处理,就放弃新数据,即,新数据帧不能覆盖旧数据帧,直至旧数据帧被处理.缺点:数据传输过快于处理速度时会掉包;好处:机制清晰,易于调试
UART4->DR; // 读取数据寄存器的数据,但不保存.主要作用:读DR时自动清理接收中断标志;
return;
}
RxTemp[cnt++] = UART4->DR ; // 把新收到的字节数据,顺序存放到RXTemp数组中;注意:读取DR时自动清零中断位;
}
// 空闲中断, 用于配合接收中断,以判断一帧数据的接收完成
if (UART4->SR & (1 << 4)) // 检查IDLE(空闲中断标志位); IDLE中断标志清理方法:序列清零,USART1 ->SR; USART1 ->DR;
{
xUSART.UART4ReceivedNum = 0; // 把接收到的数据字节数清0
memcpy(xUSART.UART4ReceivedBuffer, RxTemp, U4_RX_BUF_SIZE); // 把本帧接收到的数据,存放到全局变量xUSART.USARTxReceivedBuffer中, 等待处理; 注意:复制的是整个数组,包括0值,以方便字符串数据
xUSART.UART4ReceivedNum = cnt; // 把接收到的字节数,存放到全局变量xUSART.USARTxReceivedCNT中;
cnt = 0; // 接收字节数累计器,清零; 准备下一次的接收
memset(RxTemp, 0, U4_RX_BUF_SIZE); // 接收数据缓存数组,清零; 准备下一次的接收
UART4 ->SR;
UART4 ->DR; // 清零IDLE中断标志位!! 序列清零,顺序不能错!!
}
// 发送中断
if ((UART4->SR & 1 << 7) && (UART4->CR1 & 1 << 7)) // 检查TXE(发送数据寄存器空)、TXEIE(发送缓冲区空中断使能)
{
UART4->DR = U4TxBuffer[U4TxCounter++]; // 读取数据寄存器值;注意:读取DR时自动清零中断位;
if (U4TxCounter == U4TxCount)
UART4->CR1 &= ~(1 << 7); // 已发送完成,关闭发送缓冲区空置中断 TXEIE
}
}
/******************************************************************************
* 函 数: vUART4_GetBuffer
* 功 能: 获取UART所接收到的数据
* 参 数: uint8_t* buffer 数据存放缓存地址
* uint8_t* cnt 接收到的字节数
* 返回值: 0_没有接收到新数据, 非0_所接收到新数据的字节数
******************************************************************************/
uint8_t UART4_GetBuffer(uint8_t *buffer, uint8_t *cnt)
{
if (xUSART.UART4ReceivedNum > 0) // 判断是否有新数据
{
memcpy(buffer, xUSART.UART4ReceivedBuffer, xUSART.UART4ReceivedNum); // 把新数据复制到指定位置
*cnt = xUSART.UART4ReceivedNum; // 把新数据的字节数,存放指定变量
xUSART.UART4ReceivedNum = 0; // 接收标记置0
return *cnt; // 返回所接收到新数据的字节数
}
return 0; // 返回0, 表示没有接收到新数据
}
/******************************************************************************
* 函 数: vUART4_SendData
* 功 能: UART通过中断发送数据,适合各种数据类型
* 【适合场景】本函数可发送各种数据,而不限于字符串,如int,char
* 【不 适 合】注意环形缓冲区容量256字节,如果发送频率太高,注意波特率
* 参 数: uint8_t* buffer 需发送数据的首地址
* uint8_t cnt 发送的字节数 ,限于中断发送的缓存区大小,不能大于256个字节
* 返回值:
******************************************************************************/
void UART4_SendData(uint8_t *buf, uint8_t cnt)
{
for (uint8_t i = 0; i < cnt; i++)
U4TxBuffer[U4TxCount++] = buf[i];
if ((UART4->CR1 & 1 << 7) == 0) // 检查发送缓冲区空置中断(TXEIE)是否已打开
UART4->CR1 |= 1 << 7;
}
/******************************************************************************
* 函 数: vUART4_SendString
* 功 能: UART通过中断发送输出字符串,无需输入数据长度
* 【适合场景】字符串,长度<=256字节
* 【不 适 合】int,float等数据类型
* 参 数: char* stringTemp 需发送数据的缓存首地址
* 返回值: 元
******************************************************************************/
void UART4_SendString(char *stringTemp)
{
u16 num = 0; // 字符串长度
char *t = stringTemp ; // 用于配合计算发送的数量
while (*t++ != 0) num++; // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位
UART4_SendData((u8 *)stringTemp, num); // 调用函数完成发送,num+1:字符串以0结尾,需多发一个:0
}
// UART-4 //
/
/******************************************************************************
* 函 数: vUART5_Init
* 功 能: 初始化USART的GPIO、通信参数配置、中断优先级
* (8位数据、无校验、1个停止位)
* 参 数: uint32_t baudrate 通信波特率
* 返回值: 无
******************************************************************************/
void UART5_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 时钟使能
RCC->APB1ENR |= RCC_APB1ENR_UART5EN; // 使能UART5时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPDEN | RCC_APB2ENR_IOPCEN; // 使能GPIO时钟
// GPIO_TX引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // TX引脚工作模式:复用推挽
GPIO_Init(GPIOC, &GPIO_InitStructure);
// GPIO_RX引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // RX引脚工作模式:上拉输入; 如果使用浮空输入,引脚空置时可能产生误输入; 当电路上为一主多从电路时,可以使用复用开漏模式
GPIO_Init(GPIOD, &GPIO_InitStructure);
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 中断配置
NVIC_InitStructure .NVIC_IRQChannel = UART5_IRQn;
NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority = 2 ; // 抢占优先级
NVIC_InitStructure .NVIC_IRQChannelSubPriority = 2; // 子优先级
NVIC_InitStructure .NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
//USART 初始化设置
USART_DeInit(UART5);
USART_InitStructure.USART_BaudRate = baudrate; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 使能收、发模式
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(UART5, &USART_InitStructure); // 初始化串口
USART_ITConfig(UART5, USART_IT_TXE, DISABLE);
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE); // 使能接受中断
USART_ITConfig(UART5, USART_IT_IDLE, ENABLE); // 使能空闲中断
USART_Cmd(UART5, ENABLE); // 使能串口, 开始工作
UART5->SR = ~(0x00F0); // 清理中断
xUSART.UART5InitFlag = 1; // 标记初始化标志
xUSART.UART5ReceivedNum = 0; // 接收字节数清零
printf("\rUART5 初始化配置 接收中断、空闲中断, 发送中断\r");
}
/******************************************************************************
* 函 数: UART5_IRQHandler
* 功 能: USART2的接收中断、空闲中断、发送中断
* 参 数: 无
* 返回值: 无
******************************************************************************/
static uint8_t U5TxBuffer[256] ; // 用于中断发送:环形缓冲区,256个字节
static uint8_t U5TxCounter = 0 ; // 用于中断发送:标记已发送的字节数(环形)
static uint8_t U5TxCount = 0 ; // 用于中断发送:标记将要发送的字节数(环形)
void UART5_IRQHandler(void)
{
static uint16_t cnt = 0; // 接收字节数累计:每一帧数据已接收到的字节数
static uint8_t RxTemp[U5_RX_BUF_SIZE]; // 接收数据缓存数组:每新接收1个字节,先顺序存放到这里,当一帧接收完(发生空闲中断), 再转存到全局变量:xUSART.USARTxReceivedBuffer[xx]中;
// 接收中断
if (UART5->SR & (1 << 5)) // 检查RXNE(读数据寄存器非空标志位); RXNE中断清理方法:读DR时自动清理;
{
if ((cnt >= U5_RX_BUF_SIZE))//||xUSART.UART5ReceivedFlag==1 // 判断1: 当前帧已接收到的数据量,已满(缓存区), 为避免溢出,本包后面接收到的数据直接舍弃.
{
// 判断2: 如果之前接收好的数据包还没处理,就放弃新数据,即,新数据帧不能覆盖旧数据帧,直至旧数据帧被处理.缺点:数据传输过快于处理速度时会掉包;好处:机制清晰,易于调试
UART5->DR; // 读取数据寄存器的数据,但不保存.主要作用:读DR时自动清理接收中断标志;
return;
}
RxTemp[cnt++] = UART5->DR ; // 把新收到的字节数据,顺序存放到RXTemp数组中;注意:读取DR时自动清零中断位;
}
// 空闲中断, 用于配合接收中断,以判断一帧数据的接收完成
if (UART5->SR & (1 << 4)) // 检查IDLE(空闲中断标志位); IDLE中断标志清理方法:序列清零,USART1 ->SR; USART1 ->DR;
{
xUSART.UART5ReceivedNum = 0; // 把接收到的数据字节数清0
memcpy(xUSART.UART5ReceivedBuffer, RxTemp, U5_RX_BUF_SIZE); // 把本帧接收到的数据,存放到全局变量xUSART.USARTxReceivedBuffer中, 等待处理; 注意:复制的是整个数组,包括0值,以方便字符串数据
xUSART.UART5ReceivedNum = cnt; // 把接收到的字节数,存放到全局变量xUSART.USARTxReceivedCNT中;
cnt = 0; // 接收字节数累计器,清零; 准备下一次的接收
memset(RxTemp, 0, U5_RX_BUF_SIZE); // 接收数据缓存数组,清零; 准备下一次的接收
UART5 ->SR;
UART5 ->DR; // 清零IDLE中断标志位!! 序列清零,顺序不能错!!
}
// 发送中断
if ((UART5->SR & 1 << 7) && (UART5->CR1 & 1 << 7)) // 检查TXE(发送数据寄存器空)、TXEIE(发送缓冲区空中断使能)
{
UART5->DR = U5TxBuffer[U5TxCounter++]; // 读取数据寄存器值;注意:读取DR时自动清零中断位;
if (U5TxCounter == U5TxCount)
UART5->CR1 &= ~(1 << 7); // 已发送完成,关闭发送缓冲区空置中断 TXEIE
}
}
/******************************************************************************
* 函 数: vUART5_GetBuffer
* 功 能: 获取UART所接收到的数据
* 参 数: uint8_t* buffer 数据存放缓存地址
* uint8_t* cnt 接收到的字节数
* 返回值: 0_没有接收到新数据, 非0_所接收到新数据的字节数
******************************************************************************/
uint8_t UART5_GetBuffer(uint8_t *buffer, uint8_t *cnt)
{
if (xUSART.UART5ReceivedNum > 0) // 判断是否有新数据
{
memcpy(buffer, xUSART.UART5ReceivedBuffer, xUSART.UART5ReceivedNum); // 把新数据复制到指定位置
*cnt = xUSART.UART5ReceivedNum; // 把新数据的字节数,存放指定变量
xUSART.UART5ReceivedNum = 0; // 接收标记置0
return *cnt; // 返回所接收到新数据的字节数
}
return 0; // 返回0, 表示没有接收到新数据
}
/******************************************************************************
* 函 数: vUART5_SendData
* 功 能: UART通过中断发送数据,适合各种数据类型
* 【适合场景】本函数可发送各种数据,而不限于字符串,如int,char
* 【不 适 合】注意环形缓冲区容量256字节,如果发送频率太高,注意波特率
* 参 数: uint8_t* buffer 需发送数据的首地址
* uint8_t cnt 发送的字节数 ,限于中断发送的缓存区大小,不能大于256个字节
* 返回值:
******************************************************************************/
void UART5_SendData(uint8_t *buf, uint8_t cnt)
{
for (uint8_t i = 0; i < cnt; i++)
U5TxBuffer[U5TxCount++] = buf[i];
if ((UART5->CR1 & 1 << 7) == 0) // 检查发送缓冲区空置中断(TXEIE)是否已打开
UART5->CR1 |= 1 << 7;
}
/******************************************************************************
* 函 数: vUART5_SendString
* 功 能: UART通过中断发送输出字符串,无需输入数据长度
* 【适合场景】字符串,长度<=256字节
* 【不 适 合】int,float等数据类型
* 参 数: char* stringTemp 需发送数据的缓存首地址
* 返回值: 元
******************************************************************************/
void UART5_SendString(char *stringTemp)
{
u16 num = 0; // 字符串长度
char *t = stringTemp ; // 用于配合计算发送的数量
while (*t++ != 0) num++; // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位
UART5_SendData((u8 *)stringTemp, num); // 注意调用函数所需要的真实数据长度; 如果目标需要以0作结尾判断,需num+1:字符串以0结尾,即多发一个:0
}
#endif
// printf //
/******************************************************************************
* 功 能: printf函数支持代码
* 【特别注意】加入以下代码, 使用printf函数时, 不再需要选择use MicroLIB
* 参 数:
* 返回值:
* 备 注: 最后修改_2020年07月15日
******************************************************************************/
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
//#pragma import(__use_no_semihosting)
//struct __FILE
//{
// int handle;
//}; // 标准库需要的支持函数
//FILE __stdout; // FILE 在stdio.h文件
void _sys_exit(int x)
{
x = x; // 定义_sys_exit()以避免使用半主机模式
}
//重定向 c 库函数 printf 到串口,重定向后可使用 printf 函数
int fputc(int ch, FILE *f)
{ /* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
///重定向 c 库函数 scanf 到串口,重写向后可使用 scanf、getchar 等函数
int fgetc(FILE *f)
{
/* 等待串口输入数据 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_USARTx);
}
///*
//函数名:USART3中断服务函数
//功能: 接收数据
//注意:接收数据长度可调:RXCOUNT
//*/
//void USART3_IRQHandler(void)
//{
// u8 temp;
//
// if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
// {
// USART_ClearITPendingBit(USART3,USART_IT_RXNE);
// temp = USART_ReceiveData(USART3);
// if(temp == '\n' || RXCOUNT == 20) //判断是否接收到一个完整字符
// {
// RXCOUNT = 0;
// RXOVER =1; //接收数据完成标志位置1
// USART_ITConfig(USART3,USART_IT_RXNE,DISABLE);//失能串口接收中断标志
// }
// else
// {
// RXBUF[RXCOUNT] = temp; //依次存放到数组中
// RXCOUNT++; //字符长度变化
// }
// }
//}
main.c
/**
******************************************************************************
* @file main.c
* @author fire
* @version V1.0
* @date 2013-xx-xx
* @brief rtc 测试,显示时间格式为: xx:xx:xx
******************************************************************************
* @attention
*
* 实验平台:野火 F103-指南者 STM32 开发板
* 论坛 :http://www.firebbs.cn
* 淘宝 :https://fire-stm32.taobao.com
*
******************************************************************************
*/
#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./rtc/bsp_rtc.h"
#include "./lcd/bsp_ili9341_lcd.h"
#include "./key/bsp_key.h"
#include "./DTH11/DTH11.h"
#include "./Led/bsp_led.h"
#include "./syn6288/syn6288.h"
//变量声明
u8 RXBUF[20]; //串口存储数组
u8 RXOVER=0; //串口接收标志位
u8 RXCOUNT=0; //串口计数变量
u8 i; //清空数组变量
//时间更新函数
void Update_FrameShow (void);
//表盘框架绘制
void DrawFixed_Frame (void);
void DrawExcel (void);
void DrawCongratulate (void);
void DrawTimeFrame (void);
void DrawExternal_Environmentz (void);
void DrawWish (void);
//语音处理函数
void USART_Deal (void);
// N = 2^32/365/24/60/60 = 136 年
/*时间结构体,默认时间2024-03-02 03:20:10*/
struct rtc_time systmtime=
{
10,20,3,1,2,2024,4
};
extern __IO uint32_t TimeDisplay ;
//温度参数
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main()
{
//可使用该宏设置是否使用液晶显示
#ifdef USE_LCD_DISPLAY
ILI9341_Init (); //LCD 初始化
LCD_SetFont(&Font8x16);
LCD_SetColors(CurrentTextColor,CurrentBackColor);
ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH); /* 清屏,显示全黑 */
DrawFixed_Frame();//绘制固定物
#endif
//串口部分
USART_Config();
LED_Configuration();//LED配置
SYN6288_Init(USART2); // 初始化; USART2-PA2
USART3_Init(9600); //LD3320串口初始化
Key_GPIO_Config();
/* 配置RTC秒中断优先级 */
RTC_NVIC_Config();
RTC_CheckAndConfig(&systmtime);
DHT11_Init(); //初始化温度传感器引脚
SYN6288_Say("已开灯");//Syn6288测试
//Temperuture_Get();
while (1)
{
/* 每过1s 更新一次时间*/
if (TimeDisplay == 1)
{
/* 当前时间 */
Time_Display( RTC_GetCounter(),&systmtime);
//Time_Display1( RTC_GetCounter(),Temperature_Get(),&systmtime); //当加入温度获取时,温度获取
TimeDisplay = 0;
}
//按下按键,通过串口修改时间
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
{
struct rtc_time set_time;
/*使用串口接收设置的时间,输入数字时注意末尾要加回车*/
Time_Regulate_Get(&set_time);
/*用接收到的时间设置RTC*/
Time_Adjust(&set_time);
//向备份寄存器写入标志
BKP_WriteBackupRegister(RTC_BKP_DRX, RTC_BKP_DATA);
}
USART_Deal();
}
}
/**************************************表盘框架绘制*****************************************/
/*
函数功能: 绘制所有固定物
*/
void DrawFixed_Frame(void)
{
DrawExcel();
DrawCongratulate();
DrawTimeFrame();
DrawExternal_Environmentz();
DrawWish();
}
/*
函数功能: 绘制表格
*/
void DrawExcel(void)
{
ILI9341_DrawRectangle ( 0, 0, 240, 320, 0);
ILI9341_DrawLine(0,136,50,136);
ILI9341_DrawLine(185,136,240,136);
ILI9341_DrawLine(0,180,240,180);
ILI9341_DrawLine(0,200,240,200);
ILI9341_DrawLine(48,200,48,320);
ILI9341_DrawLine(240-48,200,240-48,320);
}
/*
函数功能: 绘制时钟表盘框架
*/
void DrawTimeFrame(void)
{
uint8_t i;
ILI9341_DrawCircle(RoundCenter_X,RoundCenter_Y,RoundRadius,0);//画外圆
ILI9341_DrawCircle(RoundCenter_X,RoundCenter_Y,RoundCenter,1); //画中心圆
//画刻度
for(i=0;i<60;i++)
{
if(i%5==0)
{//绘制圆大间距
ILI9341_DrawAngleLine(RoundCenter_X,RoundCenter_Y,6*i,RoundRadius-6,RoundRadius,RoundInterval_Color);
}
else
{//绘制圆小间距
ILI9341_DrawAngleLine(RoundCenter_X,RoundCenter_Y,6*i,RoundRadius-3,RoundRadius,RoundInterval_Color);
}
}
//OLED_WriteGRAM(); //刷新数据到OLED屏幕
}
/*
函数功能: 绘制龙年大吉
*/
void DrawCongratulate(void)
{
ILI9341_DisplayStringEx(10,10,32,32,(uint8_t *)"龙",0);
ILI9341_DisplayStringEx(240-32-10,10,32,32,(uint8_t *)"年",0);
ILI9341_DisplayStringEx(10,3*32,32,32,(uint8_t *)"大",0);
ILI9341_DisplayStringEx(240-32-10,3*32,32,32,(uint8_t *)"吉",0);
}
/*
函数功能: 绘制外部环境
*/
void DrawExternal_Environmentz(void)
{
ILI9341_DisplayStringEx(20,144,16,16,(uint8_t *)"天气:多云",0);//也可后续添加天气检测设备配置
ILI9341_DisplayStringEx(120,144,16,16,(uint8_t *)"温度:",0);//其他添加可仿照温度配置
ILI9341_DisplayStringEx(20,160,16,16,(uint8_t *)"位置:贵阳",0);//也可后续添加定位设备配置
ILI9341_DisplayStringEx(120,160,16,16,(uint8_t *)"湿度:",0);//也可后续添加空气检测设备配置
}
/*
函数功能: 绘制祝愿
*/
void DrawWish(void)
{
ILI9341_DisplayStringEx(12,210,24,24,(uint8_t *)"宜",0);
ILI9341_DisplayStringEx(204,210,24,24,(uint8_t *)"忌",0);
ILI9341_DisplayStringEx_YDir(8,240,16,16,(uint8_t *)"搞钱",0);
ILI9341_DisplayStringEx_YDir(28,240,16,16,(uint8_t *)"毕业设计",0);
ILI9341_DisplayStringEx_YDir(200,240,16,16,(uint8_t *)"晚睡晚起",0);
ILI9341_DisplayStringEx_YDir(220,240,16,16,(uint8_t *)"打游戏",0);
ILI9341_DisplayStringEx(52,210,24,24,(uint8_t *)"等待开发",0);
}
/**************************************表盘框架绘制结束*****************************************/
/*
函数功能: 更新时间框架显示,在RTC中断里调用
*/
void Update_FrameShow(void)
{
/*1. 绘制秒针、分针、时针*/
//画秒针
ILI9341_DrawAngleLine2(RoundCenter_X,RoundCenter_Y,systmtime.tm_sec*6-6-90,RoundCenter,RoundSecondHand,0);//清除之前的秒针
ILI9341_DrawAngleLine(RoundCenter_X,RoundCenter_Y,systmtime.tm_sec*6-90,RoundCenter,RoundSecondHand,RoundSecondHand_Color);
//画分针
ILI9341_DrawAngleLine2(RoundCenter_X,RoundCenter_Y,systmtime.tm_min*6-6-90,RoundCenter,RoundMiuiteHand,0);
ILI9341_DrawAngleLine(RoundCenter_X,RoundCenter_Y,systmtime.tm_min*6-90,RoundCenter,RoundMiuiteHand,RoundMiuiteHand_Color);
//画时针
ILI9341_DrawAngleLine2(RoundCenter_X,RoundCenter_Y,systmtime.tm_hour*30-30-90,RoundCenter,RoundHourHand,0);
ILI9341_DrawAngleLine(RoundCenter_X,RoundCenter_Y,systmtime.tm_hour*30-90,RoundCenter,RoundHourHand,RoundHourHand_Color);
}
/***********************************END OF FILE*********************************/
//串口处理函数
void USART_Deal(void)
{
if(RXOVER)
{
RXOVER = 0; //清除接收标志位
switch(RXBUF[0]-48)
{
case 1:GPIO_ResetBits(GPIOB,GPIO_Pin_0); //点亮小灯
SYN6288_Say("已开灯");
break;
case 2:GPIO_SetBits(GPIOB,GPIO_Pin_0); //熄灭小灯
SYN6288_Say("已关灯");
break;
default: break;
}
USART3_SendString(RXBUF); //发送给pc机上面打印显示
for(i=0;i<20;i++) //将已接收数据的数组清空:共20个字符长度
{
RXBUF[i] = 0; //重置数据缓存区
}
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//始能串口接收
}
}
液晶显示指针式时钟核心代码在上文已经放出,本文核心代码如上,本人撰写代码也需要时间,如需要全部代码或者仅需语音部分代码请私信我,感谢大家理解。