xld0932 发表于 2023-2-28 11:12

新定义NBK-EBS003 IoT功能扩展板,云端与显示的契合!

本帖最后由 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扩展板原理图:

除了软件资料外,我们硬件平台也是不可缺少的:
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);
      }

      OLED_Set_Pos(x, y + 1);

      for (i = 0; i < 8; i++)
      {
            OLED_TWI_WriteByte(0x78, 0x40, F8x16);
      }
    }
    else
    {
      OLED_Set_Pos(x, y);

      for (i = 0; i < 6; i++)
      {
            OLED_TWI_WriteByte(0x78, 0x40, F6x8);
      }
    }
}

/***********************************************************************************************************************
* @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_tESP12_QueueData;

/* 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 += 1;
    ESP12_QueueFront %= 500;

    return (Value);
}

/***********************************************************************************************************************
* @brief
* @param
* @retval
* @attention
*********************************************************************************************************************/
void QUEUE_PUSH(uint8_t Value)
{
    ESP12_QueueData = 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 = 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 = 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);
      }
    }
}

演示视频:
https://www.bilibili.com/video/BV1GA411y7s6/?share_source=copy_web&vd_source=9fd6a343652210cd5266721aa0f28c6e


软件工程源代码:


tabmone 发表于 2023-3-7 20:34

IOT可以扩展esp8266的wifi板子。

hilahope 发表于 2023-3-7 21:11

这个wifi接口的速度怎么样?            

primojones 发表于 2023-3-7 22:22

使用是易码魔盒开发的吗?            

RDS小定 发表于 2023-10-10 17:32

好贴,顶起

callhgd 发表于 2024-11-16 10:30

不错,给小哥点赞,俺 学习学习。。。
页: [1]
查看完整版本: 新定义NBK-EBS003 IoT功能扩展板,云端与显示的契合!