返回列表 发新帖我要提问本帖赏金: 80.00元(功能说明)

[APM32F4] 基于APM32F411 移植 U8g2

[复制链接]
1931|8
 楼主| kai迪皮 发表于 2023-9-13 09:49 | 显示全部楼层 |阅读模式
本帖最后由 kai迪皮 于 2023-9-13 09:50 编辑

#申请原创# @21小跑堂


1 前言

最近拿到了极海的APM32F411 TINY 板卡,APM32F411是其新推出的新品,资源如下:

基于Arm® Cortex®-M4F内核,工作主频120MHz,具有高速运算能力、多种工作模式、以及丰富的高精度外设和通讯接口;内置CRC32运算单元,可为用户提供高集成度、高可靠性的SoC方案;作为APM32F4系列MCU的超值型拓展产品,能很好的满足用户对功耗、性能、性价比方面的产品均衡需求,可适用于电力,仪器仪表,工控,家电,物联网,新能源,智慧楼宇等广泛的应用领域。

更多内容可以看他们的官网:[APM32F411 (geehy.com)](https://geehy.com/apm32?id=81)

拿到了他们的APM32F411 TINY 板卡后想着搞点事情,手上有一个0.96寸的OLED屏幕,想着拿这个板卡点亮这个屏幕,但想着点亮多没意思,于是便有了这个笔记“基于APM32F411 移植 U8g2”。刚好这个 TINY 板卡是没有屏幕显示的,后面的小伙伴也可以在我做的demo上做一些自己的应用显示。

那话不多说,现在开始吧。

2 APM32F411 源码准备

APM32F411 的评估源码可以在他们官网获取:https://geehy.com/uploads/tool/APM32F4xx_SDK_V1.4.zip

我手上的OLED使用的是I2C进行驱动的,所以我这里直接在他们的“APM32F4xx_SDK_V1.4\Examples\I2C\”目录下复制“I2C_TwoBoards_Master”demo并改名为“I2C_U8g2”。

我们将在这个demo的基础上实现U8g2适配。

image-20230905143838767.png
3 U8g2源码准备

U8g2的源码在GitHub上开源:https://github.com/olikraus/u8g2

我们把它的源码下载下来。

4 移植U8g2至APM32F411

4.1 复制U8g2源码

我们在APM32F4xx_SDK_V1.4\Middlewares下新建U8g2文件夹用于保存我们工程所需的U8g2源码。

image-20230905145606837.png
由于U8g2支持多种显示驱动的屏幕,因为源码中也包含了各个驱动对应的文件(所以不需要自己去写屏幕底层驱动了),为了减小整个工程的代码体积,我们在移植U8g2时,可以删除一些对本工程来说无用的文件

这里我们主要关注的是**U8g2库文件**中的**csrc文件**。我们把csrc文件夹中的内容添加入我们的APM32F4xx_SDK_V1.4\Middlewares\U8g2中。

4.2 工程包含U8g2源码

在工程下新建“U8g2”分组用于存放U8g2源码。

image-20230905194733092.png

由于“u8x8_d_sxxx.c”等源码是驱动屏幕的驱动文件,这里我们选用“u8x8_d_ssd1306_128x64_noname.c”,又因为u8g2_d_memory.c,u8g2_d_setup.c是必须的驱动函数所在文件这两个我们保留。(即:u8x8_d_sxxx.c样式的文件仅保留u8x8_d_ssd1306_128x64_noname.c、u8g2_d_memory.c、u8g2_d_setup.c)。

4.3 添加延时函数功能

APM32F4的SDK中已经有一个“bsp_delay.c”文件使用滴答定时器做的延时,我们包含进工程。

image-20230906095134458.png

包含进工程后,我们要把“APM_DelayTickDec()”函数放置至 apm32f4xx_int.c中的滴答定时器中断。

image-20230906095300044.png
如此一来我们便可以正常使用“APM_DelayMs”及“APM_DelayUs”函数了。

4.4  完善I2C初始化及发送函数

由于本demo使用的是I2C的主机功能,且只需要发送功能,我们把I2C的初始化函数编写如下:

  1. /*!
  2. *  I2C Init
  3. *
  4. * @param     None
  5. *
  6. * @retval    None
  7. */
  8. void I2CInit(void)
  9. {
  10.     GPIO_Config_T gpioConfigStruct;
  11.     I2C_Config_T i2cConfigStruct;

  12.     /* Enable I2C related Clock */
  13.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);
  14.     RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_I2C1);

  15.     /* Free I2C_SCL and I2C_SDA */
  16.     GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_6, GPIO_AF_I2C1);
  17.     GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_7, GPIO_AF_I2C1);

  18.     gpioConfigStruct.mode = GPIO_MODE_AF;
  19.     gpioConfigStruct.speed = GPIO_SPEED_50MHz;
  20.     gpioConfigStruct.pin = GPIO_PIN_6 | GPIO_PIN_7;
  21.     gpioConfigStruct.otype = GPIO_OTYPE_OD;
  22.     gpioConfigStruct.pupd = GPIO_PUPD_NOPULL;
  23.     GPIO_Config(GPIOB, &gpioConfigStruct);

  24.     /*  Config I2C1 */
  25.     I2C_Reset(I2C1);
  26.     i2cConfigStruct.mode = I2C_MODE_I2C;
  27.     i2cConfigStruct.dutyCycle = I2C_DUTYCYCLE_2;
  28.     i2cConfigStruct.ackAddress = I2C_ACK_ADDRESS_7BIT;
  29.     i2cConfigStruct.ownAddress1 = 0XA0;
  30.     i2cConfigStruct.ack = I2C_ACK_ENABLE;
  31.     i2cConfigStruct.clockSpeed = 100000;
  32.     I2C_Config(I2C1, &i2cConfigStruct);

  33.     I2C_DisableDualAddress(I2C1);

  34.     /* Enable I2Cx */
  35.     I2C_Enable(I2C1);
  36. }

原demo是使用字符串结束符来判断发送内容的结束的,我们简单修改一下发送函数,传参内容有:

1. 目标I2C从机地址
2. 发送数组
3. 发送数据的大小

这里我们固定I2C从机地址为7bit。

  1. /*!
  2. *   Write data to the I2C1
  3. *
  4. * @param     pBuffer: wiret buffer
  5. *
  6. * @retval    0: Error  1:Succee
  7. */
  8. uint8_t I2C_Write_Buff(uint16_t DevAddress, uint8_t *pBuffer, uint16_t Size)
  9. {
  10.     uint16_t tx_size = Size;
  11.     uint16_t I2CTimeout = I2CT_LONG_TIMEOUT;

  12.     while (I2C_ReadStatusFlag(I2C1, I2C_FLAG_BUSBSY) == SET)
  13.     {
  14.         I2CInit();

  15.         if ((I2CTimeout--) == 0)
  16.         {
  17.             return I2C_TIMEOUT_UserCallback(4);
  18.         }
  19.     }

  20.     I2C_DisableInterrupt(I2C1, I2C_INT_EVT);
  21.     /* Send START condition */
  22.     I2C_EnableGenerateStart(I2C1);

  23.     I2CTimeout = I2CT_FLAG_TIMEOUT;

  24.     while (!I2C_ReadEventStatus(I2C1, I2C_EVENT_MASTER_MODE_SELECT))  //EV5
  25.     {
  26.         if ((I2CTimeout--) == 0)
  27.         {
  28.             return I2C_TIMEOUT_UserCallback(5);
  29.         }
  30.     }

  31.     /* Send address for write */
  32.     I2C_Tx7BitAddress(I2C1, DevAddress, I2C_DIRECTION_TX);

  33.     I2CTimeout = I2CT_FLAG_TIMEOUT;

  34.     while (!I2C_ReadEventStatus(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))  //EV6
  35.     {
  36.         if ((I2CTimeout--) == 0)
  37.         {
  38.             return I2C_TIMEOUT_UserCallback(6);
  39.         }
  40.     }

  41.     /* While there is data to be written */
  42.     while (tx_size > 0u)
  43.     {
  44.         I2CTimeout = I2CT_LONG_TIMEOUT;

  45.         /* Send the current byte */
  46.         I2C_TxData(I2C1, *pBuffer);

  47.         while (!I2C_ReadEventStatus(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING))   //EV8
  48.         {
  49.             if ((I2CTimeout--) == 0)
  50.             {
  51.                 return I2C_TIMEOUT_UserCallback(8);
  52.             }
  53.         }

  54.         /* Point to the next byte to be written */
  55.         pBuffer++;
  56.         tx_size --;
  57.     }

  58.     while (!I2C_ReadEventStatus(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))   //EV8-2
  59.     {
  60.         if ((I2CTimeout--) == 0)
  61.         {
  62.             return I2C_TIMEOUT_UserCallback(9);
  63.         }
  64.     }

  65.     I2C_EnableGenerateStop(I2C1);

  66.     return 1;
  67. }

4.5 修改u8g2_d_memory.c

该源文件中包含着各个屏幕的驱动缓存,这里我们仅保留“uint8_t *u8g2_m_16_8_f(uint8_t *page_cnt)”,其他函数进行注释。

image-20230906095952829.png

4.6 修改u8g2_d_setup.c

该源文件中包含着各个屏幕的驱动缓存,这里我们仅保留“uint8_t *u8g2_m_16_8_f(uint8_t *page_cnt)”,其他函数进行注释。

image-20230906100232542.png

5 编写u8g2初始化代码

我们要使用u8g2需要对其进行一些初始化操作,我们新建 一个“apm32_u8g2.c”保存这部分内容。这里主要写一下“u8x8_byte_hw_i2c”函数内容,需要吧I2C初始化和I2C发送函数放在这里面。

  1. uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
  2. {
  3.     /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
  4.     static uint8_t buffer[128];
  5.     static uint8_t buf_idx;
  6.     uint8_t *data;

  7.     switch (msg)
  8.     {
  9.         case U8X8_MSG_BYTE_INIT:
  10.         {
  11.             /* add your custom code to init i2c subsystem */
  12.             I2CInit();
  13.         }
  14.     break;

  15.     case U8X8_MSG_BYTE_START_TRANSFER:
  16.     {
  17.         buf_idx = 0;
  18.     }
  19.     break;

  20.     case U8X8_MSG_BYTE_SEND:
  21.     {
  22.         data = (uint8_t *)arg_ptr;

  23.         while (arg_int > 0)
  24.         {
  25.             buffer[buf_idx++] = *data;
  26.             data++;
  27.             arg_int--;
  28.         }
  29.     }
  30.     break;

  31.     case U8X8_MSG_BYTE_END_TRANSFER:
  32.     {
  33.         if (I2C_Write_Buff( OLED_ADDRESS, buffer, buf_idx) != 1)
  34.             return 0;
  35.     }
  36.     break;

  37.     case U8X8_MSG_BYTE_SET_DC:
  38.         break;

  39.     default:
  40.         return 0;
  41.     }

  42.     return 1;
  43. }



6 编写OLED测试函数

OLED需要进行一些测试,如画点,画圆等操作,我这里写了一个“oled_test.c”文件进行了测试验证。因为这里面的代码比较多就不一一说明了。

7 最终效果

完成代码编写后,即可通过APM32F411TINY板卡的板载仿真器进行程序下载啦。

image-20230906144722314.png

程序运行如下所示:

tutieshi_320x180_13s.gif

代码在这里(文件名改为APM32F4xx_SDK_V1.4_I2C_U8g2.zip.001,APM32F4xx_SDK_V1.4_I2C_U8g2.zip.002再解压): APM32F4xx_SDK_V1.4_I2C_U8g2.002.zip (2.28 MB, 下载次数: 17) APM32F4xx_SDK_V1.4_I2C_U8g2.001.zip (8 MB, 下载次数: 13)


打赏榜单

21小跑堂 打赏了 80.00 元 2023-09-15
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

赞,来学习一下!  发表于 2023-10-12 11:48
@xu@xupt :**对你有所帮助,(#^.^#)  发表于 2023-9-17 11:37
很好的资源,学习啦~~  发表于 2023-9-17 08:25
@21小跑堂 :感谢支持  发表于 2023-9-15 10:29
在APM32F411 平台上移植U8g2,在OLED屏幕上实现动画显示,完成度较好,动画流畅。  发表于 2023-9-15 10:17
Tristan_C 发表于 2023-9-14 13:43 | 显示全部楼层
这个可以哦

评论

你也来试试看吧  发表于 2023-9-15 10:29
forgot 发表于 2023-9-25 11:00 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

43

主题

292

帖子

11

粉丝
快速回复 在线客服 返回列表 返回顶部