本帖最后由 xld0932 于 2023-2-28 11:14 编辑
#申请原创# @21小跑堂
NBK-EBS003 IoT功能扩展板是新定义推出的基于NBK-RD8x3x核心底板上的扩展硬件,具有丰富的功能:
1.四个常规LED灯
2.三色RGB灯
3.触控按键、触控滑条
4.OLED显示模块
5.ESP-01 Wi-Fi模块
在之前的分享帖中我们已经介绍了LED灯、RGB灯的控制方式,对于NBK-EBS003 IoT功能扩展板亦是相同的控制原理,需要注意的是,如果是将NBK-ES003 IoT扩展板直接插在NBK-RD8x3x核心底板上,需要将核心底板上的J10跳帽移动一设置选项,这样才可以使用三色RGB灯正常显示哦;
本篇要主要来分享一下使用RD8T36系列芯片的USCI0接口的TWI工作模式完成对OLED的显示驱动,UART0接口完成与ESP-01 Wi-Fi模块的通讯,最后结合这两个功能完成通过ESP-01 Wi-Fi模块连接Wi-Fi后,与知心天气服务器进行连接,获取当地的实时温度,显示在OLED上……
当然在完成上述功能的前提下,我们还需要准备一下软硬件相关的资料:
1、RD8T36P48J芯片数据手册
2、易码魔盒EasyCodeCube_RDSV3.2.5_20230105
3、新定义NBK-RD8x3x应用资料
上面3个都可以在新定义官网的下载中心进行下载:https://www.rdsmcu.com/
4、NBK-EBS003 IoT扩展板原理图:
NBK-EBS003原理图.pdf
(135.27 KB)
除了软件资料外,我们硬件平台也是不可缺少的:
1、NBK-RD8x3x核心底板
2、NBK-EBS003 IoT扩展板
3、RD LINK PRO
因为在NBK-RD8x3x核心底板上已经带有ISP下载功能模块,所以RD LINK PRO是可选择工具;ISP仅可以下载程序到MCU运行,通过实际的运行结果验证功能是否正常;而RD LINK PRO除了具有下载程序的功能之外,还带有脱机烧录、在线调试、仿真等等丰富的功能……各位小伙伴可以根据需要自行选择……
1.TWI底层驱动程序(中断方式 + 超时处理)
在TWM产生START信号、发送数据时,操作成功/完成后都会产生TWIF中断(中断打开的情况下),我们通过这个中断来判断操作是否完成,再结合TIMEOUT超时处理,这样可以保证程序不会因为通讯断开而导致程序卡死的现象,保证了程序的可靠性……
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_TWI_Ready(void)
{
USCI0_TWI_AcknowledgeConfig(DISABLE);
_nop_();
_nop_();
_nop_();
USCI0_TWI_Cmd(DISABLE);
USCI0_TWI_Cmd(ENABLE);
_nop_();
_nop_();
_nop_();
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
uint16_t OLED_TWI_Start(void)
{
uint16_t Timeout = OLED_TWI_TIMEOUT;
TWI0_InterruptFlag = 0;
USCI0_TWI_Start();
while ((0 == TWI0_InterruptFlag) && (0 != Timeout))
{
Timeout--;
}
return (Timeout);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_TWI_Stop(void)
{
_nop_();
_nop_();
_nop_();
USCI0_TWI_MasterModeStop();
_nop_();
_nop_();
_nop_();
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
uint16_t OLED_TWI_SendData(uint8_t Value)
{
uint16_t Timeout = OLED_TWI_TIMEOUT;
TWI0_InterruptFlag = 0;
USCI0_TWI_SendData(Value);
while ((0 == TWI0_InterruptFlag) && (0 != Timeout))
{
Timeout--;
}
return (Timeout);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
uint16_t OLED_TWI_WaitACK(void)
{
uint16_t Timeout = OLED_TWI_TIMEOUT;
while (RESET == USCI0_GetFlagStatus(USCI0_TWI_FLAG_TXRXnE) && (0 != Timeout))
{
Timeout--;
}
return (Timeout);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_TWI_WriteByte(uint8_t DeviceAddress, uint8_t Address, uint8_t Value)
{
OLED_TWI_Ready();
OLED_TWI_Start();
OLED_TWI_SendData(DeviceAddress);
OLED_TWI_WaitACK();
OLED_TWI_SendData(Address);
OLED_TWI_WaitACK();
OLED_TWI_SendData(Value);
OLED_TWI_WaitACK();
OLED_TWI_Stop();
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void USCI0_IRQHandler(void) interrupt 7
{
if (SET == USCI0_GetFlagStatus(USCI0_TWI_FLAG_TWIF))
{
TWI0_InterruptFlag = 1;
USCI0_ClearFlag(USCI0_TWI_FLAG_TWIF);
}
}
2.OLED驱动、配置及显示
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
OLED_TWI_WriteByte(0x78, 0x00, 0xB0 + y);
OLED_TWI_WriteByte(0x78, 0x00, ((x & 0xF0) >> 4) | 0x10);
OLED_TWI_WriteByte(0x78, 0x00, ((x & 0x0F) >> 0) | 0x00);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_Clear(void)
{
uint8_t i = 0, j = 0;
for (i = 0; i < 8; i++)
{
OLED_TWI_WriteByte(0x78, 0x00, 0xb0 + i);
OLED_TWI_WriteByte(0x78, 0x00, 0x00);
OLED_TWI_WriteByte(0x78, 0x00, 0x10);
for (j = 0; j < 128; j++)
{
OLED_TWI_WriteByte(0x78, 0x40, 0x00);
}
}
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_PartClear(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1)
{
uint8_t i = 0, j = 0;
for (i = x0; i < x1; i++)
{
for (j = y0; j < y1; j++)
{
OLED_TWI_WriteByte(0x78, 0x00, 0xB0 + i);
OLED_TWI_WriteByte(0x78, 0x00, ((j & 0xF0) >> 4) | 0x10);
OLED_TWI_WriteByte(0x78, 0x00, ((j & 0x0F) >> 0) | 0x00);
OLED_TWI_WriteByte(0x78, 0x40, 0);
}
}
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_ShowChar(uint8_t x, uint8_t y, char ch, uint8_t CharSize)
{
uint8_t i = 0;
ch -= 0x20;
if (x > (128 - 1))
{
x = 0;
y = y + 2;
}
if (CharSize == 16)
{
OLED_Set_Pos(x, y);
for (i = 0; i < 8; i++)
{
OLED_TWI_WriteByte(0x78, 0x40, F8x16[ch * 16 + i]);
}
OLED_Set_Pos(x, y + 1);
for (i = 0; i < 8; i++)
{
OLED_TWI_WriteByte(0x78, 0x40, F8x16[ch * 16 + i + 8]);
}
}
else
{
OLED_Set_Pos(x, y);
for (i = 0; i < 6; i++)
{
OLED_TWI_WriteByte(0x78, 0x40, F6x8[ch][i]);
}
}
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_ShowString(uint8_t x, uint8_t y, char *str, uint8_t CharSize)
{
unsigned char j = 0;
while (*str != '\0')
{
OLED_ShowChar(x, y, *str, CharSize);
x += 8;
if (x > 120)
{
x = 0;
y += 2;
}
str++;
}
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_Init(void)
{
TWI0_InterruptFlag = 0;
GPIO_Init(GPIO0, GPIO_PIN_5, GPIO_MODE_IN_PU);
USCI0_TWI_GeneralCallCmd(ENABLE);
USCI0_TWI_AcknowledgeConfig(ENABLE);
USCI0_ITConfig(ENABLE, LOW);
USCI0_TWI_MasterCommunicationRate(USCI0_TWI_32);
USCI0_TWI_Cmd(ENABLE);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void OLED_Configure(void)
{
OLED_TWI_WriteByte(0x78, 0x00, 0xAE);
OLED_TWI_WriteByte(0x78, 0x00, 0x00);
OLED_TWI_WriteByte(0x78, 0x00, 0x10);
OLED_TWI_WriteByte(0x78, 0x00, 0x40);
OLED_TWI_WriteByte(0x78, 0x00, 0xB0);
OLED_TWI_WriteByte(0x78, 0x00, 0x81);
OLED_TWI_WriteByte(0x78, 0x00, 0xFF);
OLED_TWI_WriteByte(0x78, 0x00, 0xA1);
OLED_TWI_WriteByte(0x78, 0x00, 0xA6);
OLED_TWI_WriteByte(0x78, 0x00, 0xA8);
OLED_TWI_WriteByte(0x78, 0x00, 0x3F);
OLED_TWI_WriteByte(0x78, 0x00, 0xC8);
OLED_TWI_WriteByte(0x78, 0x00, 0xD3);
OLED_TWI_WriteByte(0x78, 0x00, 0x00);
OLED_TWI_WriteByte(0x78, 0x00, 0xD5);
OLED_TWI_WriteByte(0x78, 0x00, 0x80);
OLED_TWI_WriteByte(0x78, 0x00, 0xD8);
OLED_TWI_WriteByte(0x78, 0x00, 0x05);
OLED_TWI_WriteByte(0x78, 0x00, 0xD9);
OLED_TWI_WriteByte(0x78, 0x00, 0xF1);
OLED_TWI_WriteByte(0x78, 0x00, 0xDA);
OLED_TWI_WriteByte(0x78, 0x00, 0x12);
OLED_TWI_WriteByte(0x78, 0x00, 0xDB);
OLED_TWI_WriteByte(0x78, 0x00, 0x30);
OLED_TWI_WriteByte(0x78, 0x00, 0x8D);
OLED_TWI_WriteByte(0x78, 0x00, 0x14);
OLED_TWI_WriteByte(0x78, 0x00, 0xAF);
}
3.UART0底层驱动程序
UART0配置了中断方式,通过中断标志位,来实现发送数据完成的判断,接收到新数据的缓存操作……
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void ESP12_Init(void)
{
QUEUE_INIT();
ESP12_InitBuffer();
GPIO_Init(GPIO2, GPIO_PIN_0, GPIO_MODE_IN_PU);
GPIO_Init(GPIO2, GPIO_PIN_1, GPIO_MODE_IN_PU);
UART0_Init(32000000, 115200, UART0_Mode_10B, UART0_CLOCK_TIMER1, UART0_RX_ENABLE);
UART0_ITConfig(ENABLE, LOW);
TimeSliceStartup(ESP12_TxHandler, 5000);
TimeSliceStartup(ESP12_RxHandler, 5);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void UART0_IRQHandler(void) interrupt 4
{
if (SET == UART0_GetFlagStatus(UART0_FLAG_RI))
{
QUEUE_PUSH(UART0_ReceiveData8());
UART0_ClearFlag(UART0_FLAG_RI);
}
if (SET == UART0_GetFlagStatus(UART0_FLAG_TI))
{
UART0_ClearFlag(UART0_FLAG_TI);
UART0_TI_Flag = 1;
}
}
4.消息队列代码实现
使用消息队列是用来暂存UART0接收到的数据,对于接收到的数据不要中断函数中进行处理,而是先缓存起来,这样在接收大量数据时,保证了接收数据不会丢失(接收缓存大小满足的情况下),保证程序处理消息的正确性,随后在应用中对消息队列中的数据再进行解析处理……
/* Private variables **************************************************************************************************/
uint16_t ESP12_QueueFront = 0;
uint16_t ESP12_QueueRear = 0;
uint8_t ESP12_QueueData[600];
/* Private functions **************************************************************************************************/
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void QUEUE_INIT(void)
{
ESP12_QueueFront = 0;
ESP12_QueueRear = 0;
memset(ESP12_QueueData, 0, sizeof(ESP12_QueueData));
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
uint8_t QUEUE_EMPTY(void)
{
if (ESP12_QueueFront == ESP12_QueueRear)
{
return (1);
}
else
{
return (0);
}
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
uint8_t QUEUE_POP(void)
{
uint8_t Value = ESP12_QueueData[ESP12_QueueFront];
ESP12_QueueFront += 1;
ESP12_QueueFront %= 500;
return (Value);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void QUEUE_PUSH(uint8_t Value)
{
ESP12_QueueData[ESP12_QueueRear] = Value;
ESP12_QueueRear += 1;
ESP12_QueueRear %= 500;
}
5.ESP-01 Wi-Fi驱动程序
ESP-01模块实现主要2部分,一部分是对于AT指令的处理,用于配置ESP-01模块的参数,比如连接Wi-Fi网络、连接服务器等;另一部分就是在与服务器建议通讯连接后,与服务器之间的消息通讯;这两部分都有不同的解析和处理方式,都是基于上述的消息队列来实现的……
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void ESP12_InitBuffer(void)
{
ESP12_Length = 0;
memset(ESP12_Buffer, 0, sizeof(ESP12_Buffer));
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
uint16_t ESP12_SendData(uint8_t Value)
{
uint16_t Timeout = ESP12_UART_TIMEOUT;
UART0_TI_Flag = 0;
UART0_SendData8(Value);
while ((0 == UART0_TI_Flag) && (0 != Timeout))
{
Timeout--;
}
return (Timeout);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void ESP12_SendString(char *str)
{
while (*str)
{
ESP12_SendData(*str++);
}
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
uint8_t ESP12_AT_WaitForReply(char *Keyword)
{
while (QUEUE_EMPTY() == 0)
{
ESP12_Buffer[ESP12_Length++] = QUEUE_POP();
if (strstr((char *)ESP12_Buffer, Keyword) != NULL)
{
return (1);
}
}
return (0);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
uint16_t ESP12_AT_SendCommand(char *Command, char *Response, uint16_t Timeout)
{
if (Command != NULL)
{
QUEUE_INIT();
ESP12_SendString(Command);
ESP12_SendString("\r\n");
}
if (0 != Timeout)
{
ESP12_InitBuffer();
while (Timeout--)
{
if (0 != ESP12_AT_WaitForReply(Response))
{
return (1);
}
TimeSliceDelayMS(1);
}
return (0);
}
return (1);
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void ESP12_Configure(void)
{
uint8_t Result = 0;
OLED_Clear();
OLED_ShowString(0, 0, "Step:01", 16);
OLED_ShowString(0, 2, "ESP12 Configure", 16);
TimeSliceDelayMS(500);
do
{
TimeSliceDelayMS(10);
Result = ESP12_AT_SendCommand("AT", "OK", 100);
} while (0 == Result);
OLED_Clear();
OLED_ShowString(0, 0, "Step:02", 16);
OLED_ShowString(0, 2, "AT->OK", 16);
TimeSliceDelayMS(500);
/* 开回显 */
ESP12_AT_SendCommand("ATE1", "OK", 100);
OLED_Clear();
OLED_ShowString(0, 0, "Step:03", 16);
OLED_ShowString(0, 2, "ATE1", 16);
TimeSliceDelayMS(500);
/* 查询版本信息 */
ESP12_AT_SendCommand("AT+GMR", "OK", 100);
OLED_Clear();
OLED_ShowString(0, 0, "Step:04", 16);
OLED_ShowString(0, 2, "AT+GMR", 16);
TimeSliceDelayMS(500);
/* 设置当前Wi-Fi模式: Station模式 */
ESP12_AT_SendCommand("AT+CWMODE=1", "OK", 100);
OLED_Clear();
OLED_ShowString(0, 0, "Step:05", 16);
OLED_ShowString(0, 2, "Station Mode", 16);
TimeSliceDelayMS(500);
/* 连接WiFi */
if (ESP12_AT_SendCommand("AT+CWJAP=\"********\",\"********\"", "OK", 8000) == 0)
{
if (ESP12_AT_SendCommand(NULL, "OK", 8000) == 0)
{
OLED_Clear();
OLED_ShowString(0, 0, "WiFi Connect Error!", 16);
return;
}
}
OLED_Clear();
OLED_ShowString(0, 0, "Step:06", 16);
OLED_ShowString(0, 2, "WiFi Connect OK", 16);
TimeSliceDelayMS(500);
ESP12_WiFiFlag = 1;
/* 查询IP地址 */
ESP12_AT_SendCommand("AT+CIFSR", "OK", 100);
OLED_Clear();
OLED_ShowString(0, 0, "Step:07", 16);
OLED_ShowString(0, 2, "Get IP", 16);
TimeSliceDelayMS(500);
/* 设置单链接 */
ESP12_AT_SendCommand("AT+CIPMUX=0", "OK", 100);
OLED_Clear();
OLED_ShowString(0, 0, "Step:08", 16);
OLED_ShowString(0, 2, "AT+CIPMUX=0", 16);
TimeSliceDelayMS(500);
/* 连接服务器 */
if (ESP12_AT_SendCommand("AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80", "OK", 8000) == 0)
{
OLED_Clear();
OLED_ShowString(0, 0, "Server Connect Error!", 16);
return;
}
OLED_Clear();
OLED_ShowString(0, 0, "Step:09", 16);
OLED_ShowString(0, 2, "Server Connect OK", 16);
TimeSliceDelayMS(500);
ESP12_ServerFlag = 1;
/* 透明传输 */
ESP12_AT_SendCommand("AT+CIPMODE=1", "OK", 100);
OLED_Clear();
OLED_ShowString(0, 0, "Step:10", 16);
OLED_ShowString(0, 2, "AT+CIPMODE=1", 16);
TimeSliceDelayMS(500);
/* 开始传输 */
if (ESP12_AT_SendCommand("AT+CIPSEND", ">", 1000) == 0)
{
OLED_Clear();
OLED_ShowString(0, 0, "Transfer Not Ready...", 16);
return;
}
OLED_Clear();
OLED_ShowString(0, 0, "Step:11", 16);
OLED_ShowString(0, 2, "Transfer Ready", 16);
TimeSliceDelayMS(500);
ESP12_InitFlag = 1;
}
6.OLED显示实时温度
OLED在配置ESP-01的步骤时,会进行相应的提示;在与知心天气服务器建立通讯后,会定期的向服务器发送获取信息的请求,服务器在收到请求后会核对信息和密钥,然后返回数据;返回的数据是JSON格式的,在C51中我们为了节省空间和操作,直接使用字符串的函数进行提取操作,并显示相应的信息到OLED显示屏上……
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void ESP12_TxHandler(void)
{
if (1 == ESP12_InitFlag)
{
/* 每间隔5秒获取一次天气信息, 服务器限制每分钟最多20次!!! */
ESP12_SendString("GET https://api.seniverse.com/v3/weather/now.json?key=*****************&location=shanghai&language=zh-Hans&unit=c\r\n");
}
}
/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void ESP12_RxHandler(void)
{
static uint8_t BraceCount = 0;
static uint8_t BraceState = 0;
uint8_t RxData = 0;
if (0 == QUEUE_EMPTY())
{
if (0 == BraceState)
{
BraceState = 1;
BraceCount = 0;
ESP12_InitBuffer();
}
RxData = QUEUE_POP();
ESP12_Buffer[ESP12_Length++] = RxData;
switch (RxData)
{
case 0x7B:
BraceCount++;
break;
case 0x7D:
BraceCount--;
if (0 == BraceCount)
{
BraceState = 2;
}
break;
default:
break;
}
if (ESP12_Length >= sizeof(ESP12_Buffer))
{
ESP12_Length = 0;
}
}
else
{
if (2 == BraceState)
{
BraceState = 0;
OLED_ShowString(0, 0, strstr((char *)ESP12_Buffer, "\"temperature\""), 16);
}
}
}
演示视频:
软件工程源代码:
Project_NBK-RD8x3x_NBK-EBS003.zip
(204.14 KB)
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
打赏榜单
21小跑堂 打赏了 80.00 元 2023-03-06 理由:恭喜通过原创审核!期待您更多的原创作品~
|
结构清晰,代码详细,实现效果较好,视频展示也很不错,采用AT指令方式与模块通信,简单快捷。