MSP432学习02--建立自己的oled硬件库

本文介绍如何为MSP432平台上的OLED屏幕(SSD1306)开发驱动程序,涵盖硬件I2C接口配置、数据手册解析及关键函数实现等核心内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:第一篇入门创建工程:MSP432学习01--新建keil空工程_Kindred。的博客-优快云博客

本文所会用到的附件

链接:https://pan.baidu.com/s/1nuaua9iP-zPtb_3-ATP2gw?pwd=hr7j 
提取码:hr7j

看这一节的前提是你已经建立好相应的工程文件,并且对于MSP432官方的驱动库有一定的熟悉程度。这篇文章旨在分享对于一个陌生的模块,如何创建一个属于自己的硬件库的过程。

最开始是创建文件过程,

新建.c和.h文件,以及将他们添加进项目,这里不再赘述,相信大家应该都清楚,主要强调一个习惯:每一个代码文件和每一个函数都要有自己的说明,这样不仅是为了别人的可读性,也为了自己隔几天还能看懂QAQ

以oled屏幕(SSD1306)的驱动为例,这里使用硬件I2C驱动。

这里记两个小知识点:

1.首先是SPI总线和I2C总线的区别(参考I2C和SPI简介_sunxiaolin2016的博客-优快云博客_i2c与spi

I2C线序:

  • SDA(串行数据线),I2C总线的数据信号线;
  • SCL(串行时钟线),时钟信号线。
  • VCC,电压,典型电压为+5V或+3.3V
  • GND,接地

SPI线序:

  • SCLK,串行时钟(主机输出)
  • SSEL,Slave,从设备使能信号,由主设备控制
  • MOSI,Master output Slave input, 主设备数据输出,从设备数据输入
  • MISO,Master input Slave output,主设备数据输入,从设备数据输出
  • GND,接地

I2C工作原理
I2C总线在传送数据过程中共有三种类型信号,分别是:开始信号,结束信号和应答信号。
开始信号,SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号,SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
应答信号,接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已经收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未能收到应答信号,则判断为受控单元出现故障。

SPI工作原理
为了开始通信,总线主控器使用从属设备支持的频率来配置时钟。然后,主机在选择线上选择逻辑电平为0的从机。如果需要等待时间,例如模数转换,则主机必须在发出时钟周期之前至少等待该时间段。

在每个SPI时钟周期内,都会发生全双工数据传输。主机在MOSI线上发送一个位,从机读取它;而从机在MISO线上发送一个位,主机读取它。

传输通常涉及两个给定字长的移位寄存器,例如八位,一个在主机中,一个在从机中。它们以虚拟环形拓扑连接。数据通常先移出最高有效位。在时钟沿,主机和从机都移出一点并在传输线上输出到对方。在下一个时钟沿,在每一个接收器处,从传输线采样该位,并将其设置为移位寄存器的新的最低有效位。在寄存器位移入和移出后,主设备和从设备已交换了寄存器值。如果需要交换更多数据,请重新加载位移寄存器,然后重复该过程。传输可以持续任何数量的时钟周期。完成后,主机停止切换时钟信号。

传输通常由八位字组成。但是,其他字长也很常见。例如,用于触摸屏控制器或者音频解码器的16位字,也有一些ADC是12位字。

————————————————

2.然后下面是硬件I2C和模拟I2C的区别(参考硬件I2C与模拟I2C_JYU_hsy的博客-优快云博客_硬件i2c

硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,因而效率要远高于软件模拟的I2C;一般也较为稳定,但是程序较为繁琐。

硬件(固件)I2C是直接调用内部寄存器进行配置;而软件I2C是没有寄存器这个概念的。

软件I2C一般是使用GPIO管脚,用软件控制SCL,SDA线输出高低电平,模拟i2c协议的时序。

主要对比:

1.硬件IIC用法比较复杂,模拟IIC的流程更清楚一些。

2.硬件IIC速度比模拟快,并且可以用DMA

3.模拟IIC可以在任何管脚上,而硬件只能在固定管脚上。


再讲OLED屏幕显示的原理(主要是数据储存和显示的形式与方法):

数据存储在oled内置的RAM中,我们常见的寻址方式是这种:

 也就是说一页实际上对应了屏幕上的8行,而实际上只能选择精确到从哪一页开始(0-7)而不是从哪一行开始(0-63),这也导致了可以显示的字体高度是8的整数倍。


先放上SSD1306的手册,锻炼自己阅读手册的能力,接下来需要用到:https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf

举几个典型的函数的例子:

首先是OLED_WR_Byte函数,之后会一直用到这个函数,本质是用I2C发送数据,这个函数起到的作用是判定这个字节是命令还是数据,参考下面的数据手册的内容,代码如下

//发送一个字节
//向SSD1306写入一个字节。
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(uint8_t dat, uint8_t mode)
{
  if (mode)
    MAP_I2C_masterSendMultiByteStart(EUSCI_BX, 0x40);
  else
    MAP_I2C_masterSendMultiByteStart(EUSCI_BX, 0x00);
  MAP_I2C_masterSendMultiByteFinish(EUSCI_BX, dat);
}

 接着是开启oled显示的函数OLED_Display_On(void),根据数据手册代码如下

0XA6:正常显示,0XA7反显

 /**
 *@brief reverse the color
 *@param 0:normal 1:reverse
 *@retval None
 */
//反显函数
void OLED_ColorTurn(uint8_t i)
{
  if (i == 0)
  {
    OLED_WR_Byte(0xA6, OLED_CMD); //正常显示
  }
  if (i == 1)
  {
    OLED_WR_Byte(0xA7, OLED_CMD); //反色显示
  }
}

最后是在指定位置显示一个字符 OLED_ShowChar,需要用到另一个函数OLED_Set_Pos,先解释OLED_Set_Pos,0xb0+y即从第y页开始,根据前面讲到的oled的显存共8页(0-7),每页128列(0-127),如设置第100行,x=0x64,低位(x & 0xf0) >> 4) | 0x10 = 0x16,取6,高位(x & 0x0f) = 0x04,取4,最后即为0x64,第100行。代码如下:

 /**
 *@brief set the coordinates(before displaying)
 *@param x[0, 127], y[0,7] position in the oled RAM
 *@retval None
 */
//坐标设置
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
  OLED_WR_Byte(0xb0 + y, OLED_CMD);
  OLED_WR_Byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD);
  OLED_WR_Byte((x & 0x0f), OLED_CMD);
}

再解释 OLED_ShowChar,这里会用到oledfont.h(会统一打包到附件里),里面是已经做好了的各种常见字母数字符号的点阵序列,我们只需要找到需要的字母的索引即可。

 /**
 *@brief display a character
 *@param x:0-127, y:0-7, chr: the character, sizey: the size of it, actually vertical size
 *@retval None
 */
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~7
//sizey:选择字体 6x8  8x16
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t sizey)
{
  uint8_t c = 0, sizex = sizey / 2;
  uint16_t i = 0, size1;
  if (sizey == 8)
    size1 = 6;
  else
    size1 = (sizey / 8 + ((sizey % 8) ? 1 : 0)) * (sizey / 2);
  c = chr - ' '; //得到偏移后的值
  OLED_Set_Pos(x, y);
  for (i = 0; i < size1; i++)
  {
    if (i % sizex == 0 && sizey != 8)
      OLED_Set_Pos(x, y++);
    if (sizey == 8)
      OLED_WR_Byte(asc2_0806[c][i], OLED_DATA); //6X8字号
    else if (sizey == 16)
      OLED_WR_Byte(asc2_1608[c][i], OLED_DATA); //8x16字号
    //		else if(sizey==xx) OLED_WR_Byte(asc2_xxxx[c][i],OLED_DATA);//用户添加字号
    else
      return;
  }
}


仍然在此鸣谢b站@m-RNA,我的工程文件大部分参考于他,他在b站有MSP432的快速入门视频和相应资料:MSP432P4电赛入门速成/Keil/CCS/VScode_哔哩哔哩_bilibili

还有前文的参考资料也已经标明出处。

限于本人水平,肯定有很多疏漏,希望大家多多指教!

MSP432 低功耗高性能并存10.1 Digital I/O Introduction The digital I/O features include: • Independently programmable individual I/Os • Any combination of input or output • Individually configurable interrupts for ports (available for certain ports only) • Independent input and output data registers • Individually configurable pullup or pulldown resistors • Wake-up capability from ultra-low power modes (available for certain ports only) • Individually configurable high drive I/Os (available for certain I/Os only) Devices within the family may have up to eleven digital I/O ports implemented (P1 to P10 and PJ). Most ports contain eight I/O lines; however, some ports may contain less (see the device-specific data sheet for ports available). Each I/O line is individually configurable for input or output direction, and each can be individually read or written. Each I/O line is individually configurable for pullup or pulldown resistors. Certain ports have interrupt and wake-up capability from ultra-low power modes (see device specific data sheet for ports with interrupt and wake-up capability). Each interrupt can be individually enabled and configured to provide an interrupt on a rising or falling edge of an input signal. All interrupts are fed into an encoded Interrupt Vector register, allowing the application to determine which sub-pin of a port has generated the event. Individual ports can be accessed as byte-wide ports or can be combined into half-word-wide ports. Port pairs P1 and P2, P3 and P4, P5 and P6, P7 and P8, and so on, are associated with the names PA, PB, PC, PD, and so on, respectively. All port registers are handled in this manner with this naming convention. The main exception are the interrupt vector registers, for example, interrupts for ports P1 and P2 must be handled through P1IV and P2IV, PAIV does not exist. When writing to port PA with half-word operations, all 16 bits are written to the port. When writing to the lower byte of port PA using byte operations, the upper byte remains unchanged. Similarly, writing to the upper byte of port PA using byte instructions leaves the lower byte unchanged. When writing to a port that contains less than the maximum number of bits possible, the unused bits are don't care. Ports PB, PC, PD, PE, and PF behave similarly.
<think>好的,我需要帮助用户找到适用于MSP430F5529的UC1611S显示屏驱动函数库。首先,用户提供的引用中提到了MSP430F5529的DriverLib库和一些自制的函数库,比如I2C驱动OLED和UART配置。但UC1611S可能使用不同的接口,比如SPI或I2C,所以我得先确认UC1611S的通信协议。 用户提到的引用[1]是关于I2C驱动OLED的,引用[3]提到了UART和ADC的库函数。UC1611S通常使用SPI或8位并行接口,所以可能需要SPI或GPIO的函数库。MSP430F5529的DriverLib支持这些外设,但用户可能需要根据数据手册编写特定指令。 接下来,我需要检查是否有现成的库。引用[2]提到有自制的常用函数库,但不确定是否包含UC1611S。可能需要用户自己移植或参考类似显示屏的驱动,比如OLED的I2C代码,调整通信协议和初始化命令。 另外,用户需要配置GPIO模拟时序或使用硬件SPI。DriverLib的SPI函数和GPIO操作在引用[1]和[3]中都有涉及,可以指导用户如何使用这些函数发送数据和命令。 最后,建议用户查阅UC1611S的数据手册,编写初始化序列,并参考现有的I2C或SPI例程进行适配。可能需要结合已有的库函数,比如USCI模块的配置,来构建驱动代码。</think>针对MSP430F5529驱动UC1611S显示屏的需求,以下是系统化的解决方案: ### 一、驱动开发基础 1. **硬件接口确认** UC1611S支持4线SPI/8位并行/I2C接口,需根据电路连接确定通信协议。MSP430F5529的USCI模块支持SPI/I2C模式[^1][^3]。 2. **开发环境准备** 建议使用Code Composer Studio(CCS)并导入MSP430F5529 DriverLib库,该库包含GPIO、SPI、I2C等底层驱动函数[^1][^2]。 ### 二、核心驱动实现 ```c // 示例代码框架(SPI模式) #include "msp430.h" #include "driverlib.h" #define DC_PIN BIT4 // P3.4作为数据/命令选择 #define CS_PIN BIT5 // P3.5作为片选 void UC1611_WriteCommand(uint8_t cmd) { GPIO_setOutputLowOnPin(GPIO_PORT_P3, DC_PIN); // 命令模式 GPIO_setOutputLowOnPin(GPIO_PORT_P3, CS_PIN); USCI_B_SPI_transmitData(UCB0_BASE, cmd); // 使用USCI_B0 SPI GPIO_setOutputHighOnPin(GPIO_PORT_P3, CS_PIN); } void UC1611_Init() { // 硬件SPI初始化 GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN5 | GPIO_PIN6 | GPIO_PIN7); // SPI CLK/MISO/MOSI USCI_B_SPI_initMasterParam param = {0}; param.selectClockSource = USCI_B_SPI_CLOCKSOURCE_SMCLK; param.clockSourceFrequency = UCS_getSMCLK(); param.desiredSpiClock = 1000000; // 1MHz param.msbFirst = USCI_B_SPI_MSB_FIRST; USCI_B_SPI_initMaster(UCB0_BASE, &param); USCI_B_SPI_enable(UCB0_BASE); // GPIO初始化 GPIO_setAsOutputPin(GPIO_PORT_P3, DC_PIN | CS_PIN); GPIO_setOutputHighOnPin(GPIO_PORT_P3, CS_PIN); // 发送初始化指令序列 UC1611_WriteCommand(0xE2); // 系统复位 __delay_cycles(1000); UC1611_WriteCommand(0xA3); // 偏置设置 // 更多初始化指令参考数据手册... } ``` ### 三、关键实现步骤 1. **时序匹配** UC1611S要求SPI模式最大时钟频率10MHz,需通过UCSCTL4寄存器配置SMCLK分频[^3] 2. **显存管理** 需要建立显示缓存数组(160x160分辨率对应3200字节),通过`UC1611_WriteData()`批量传输 3. **功耗优化** 使用LPM3低功耗模式,唤醒后通过`UC1611_WriteCommand(0xAF)`恢复显示[^1] ### 四、调试建议 1. 使用逻辑分析仪抓取SPI波形 2. 先验证GPIO模拟时序,再迁移到硬件SPI 3. 重点检查初始化序列中的电压调节命令(0x24/0x81/0x82) 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值