MSP430入门


使用的板为TI的MSP430F5529套件
在这里插入图片描述
在这里插入图片描述

GPIO

GPIO的特性:

  • 可以独立控制每个GPIO口的方向(输入/输出模式)
  • 可以独立设置每个GPIO的输出状态(高/低电平)
  • 所有GPIO口在复位后都有一个默认方向

P1—P4具有输入输出、中断和外部功能模块。每个I/O引脚单独配置输入或输出方向,每个引脚可以单独读或写。P1—P8端口具有中断功能,每一个I/O引脚的中断可以单独启用和配置。

实验一、单个按键对LED灯的控制

通过按键对IO口P1.0的操作实现对LED灯的亮灭控制

端口寄存器的说明

名称缩写BIT = 1BIT = 0
方向寄存器PxDIR输出模式输入模式
输入寄存器PxIN输入高电平输入低电平
输出寄存器PxOUT输出高电平输出低电平
上下拉电阻使能寄存器PxREN使能禁用
功能选择寄存器PxSEL外设功能IO端口
驱动强度寄存器PxDS高强度低强度
中断使能寄存器PxIE允许中断禁止中断
中断标志寄存器PxIFG有中断请求无中断请求

通过上表可知,在读写IO口之前要先设置方向寄存器(PxDIR)的模式。读IO,实际上就是读输入寄存器(PxIN);写IO,实际上就是写输出寄存器(PxOUT)
根据输出电平的“强弱”分类,可将 IO 输出分为推挽/推拉输出、上拉电阻输出、下拉电阻输出 3 种。(此处有强弱1强弱0之分)

MSP430G5529.h中有如下宏定义

#define BIT0                (0x0001u)
#define BIT1                (0x0002u)
#define BIT2                (0x0004u)
#define BIT3                (0x0008u)
#define BIT4                (0x0010u)
#define BIT5                (0x0020u)
#define BIT6                (0x0040u)
#define BIT7                (0x0080u)
#define BIT8                (0x0100u)
#define BIT9                (0x0200u)
#define BITA                (0x0400u)
#define BITB                (0x0800u)
#define BITC                (0x1000u)
#define BITD                (0x2000u)
#define BITE                (0x4000u)
#define BITF                (0x8000u)

MSP430的按"位"操作
这里采用的"按位"操作并不是指MSP430具备了位操作的能力,按位操作实际上是对整个字节都进行了"操作",只不过对其中的7位的值没有影响而已。
写位操作
For example:

P1OUT |= 0x01;	// 将 P1.0 置 1 ,按位“或”,相当于置 1
P1OUT &= ~0x02;	// 将 P1.1 置0,取反后再按位“与”,相当于置 0
P1OUT ^= 0x04;	// 将 P1.2 取反,按位“异或”相当于取反

由上述中的宏定义,对“位”操作也可改写为:

P1OUT |= BIT0;           //按位“或”,相当于置1	
P1OUT &= ~BIT1;          //取反后再按位“与”,相当于置0
P1OUT ^= BIT2;           //按位“异或”,相当于取反

另外,也能够通过加号对多位同时操作。例如要将 P1.0、P1.1、P1.2 均置 1,不影响其他位:

P1OUT |= BIT0 + BIT1 + BIT2;

读位操作
读位操作是通过 if 语句的判断得到的。这种方法同样不意味着MSP430能够对位进行读取,这种方法同样需要对一个字节的 8 位进行操作

unsigned char temp = 0;

if( ( P1IN & BIT1 ) == 0 )
	{
		P2OUT |= BIT0;	// 读取 P1.1 的状态写入 P2.0
	}
else
{
	P2OUT &= ~BIT0;
}
if( ( P1IN & BIT0 ) == 0 )
{
	temp = 1;	// 读取 P1.0 的状态写入 temp
}
else
{
	temp = 0;	
}

单个按键对LED灯的控制

#include "msp430f5529.h"

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;
  
  P1DIR |= BIT0;        // 设置 P1.0 为输出模式
  P2REN |= BIT1;        // 使能 P2.1 为上下拉电阻功能
  P2OUT |= BIT1;        // 设置 P2.1 为上拉电阻输出方式
  
  while(1)
  {
    	if(P2IN & BIT1)     	// 判断按键是否按下,按下时为 P2.1 = 0,抬起时 p2.1 = 1(看下面的原理图)
	    {
	      	P1OUT |= BIT0;    	// 当抬起时,P1.0 输出高电平
	    }
	    else
	    {
	      	P1OUT &= ~BIT0;		// 当按下时,P1.0 输出低电平
	  	}
  }
}

如下图原理图所示按键按下时与 GND 相连所以相当于置 0

MSP430时钟系统

MSP430单片机的时钟分为MCLKSMCLKACLK三个。三种时钟的功能区别如下:

  1. MCLK:主时钟(Main SystemClock),专为CPU运行提供的时钟。MCLK频率配置的越高,CPU执行的速度越快。虽然CPU速度越快功耗也越高,但高频率的MCLK可以让CPU工作时间更短。所以正确的低功耗设计并不是要尽量降低MCLK,而是在不用CPU时立刻关闭MCLK。在大部分应用中,需要CPU运算的时间都非常短,所以,间歇开启MCLK(唤醒CPU)的方法节能效果非常明显。
  2. SMCLK:子系统时钟(Sub-mainClock),专为一些需要高速时钟的片内外设提供服务,比如定时器和ADC采样等。当CPU休眠时,只要SMCLK开启,定时器和ADC仍可工作(一般待片内外设完成工作后触发中断,唤醒CPU去做后续工作)。
  3. ACLK:辅助时钟(AuxiliaryClock),辅助时钟的频率很低,所以即使一直开启功耗也不大,当然关掉也是可以的。辅助时钟可以供给那些只需低频时钟的片内外设,比如LCD控制器,还可用于产生节拍时基,与定时器配合间歇唤醒CPU。

中断

按键LED中断程序

#include <msp430.h>

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;             

  P1DIR |= BIT0;       // 设置为输出                 
  P2OUT |= BIT1;       // 设置为上拉输出                 
  P2REN |= BIT1;       // 上下拉使能                
  P2IE |= BIT1;        // 局部中断使能(打开局部中断)      
  P2IES |= BIT1;       // 选择边沿触发,从高电平到低电平(下降沿触发)
  P2IFG &= ~BIT1;      // 清除中断标志(对于多源中断来说必须先请中断标志再打开中断,也是为了确保初始化后的标志位不会触发中断)      

  _BIS_SR(GIE);        // 启用全局中断  

  while (1)
  {
    __no_operation();   // Do nothing
  }
}

// Port 1 interrupt ser-vice routine
#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void)
{
  P1OUT ^= BIT0;        // 取反
  P2IFG &= ~BIT1;       // 清除中断标志
}

#pragma vector=PORT1_VECTOR
#Pragma是编译器指令,是告诉编译器将函数与中断向量连接起来。“vector=”后面是中断向量地址的宏定义,例如P1口中断就是PORT1_VECTOR,定时器中断就是TIMER0_A1_VECTOR。

__interrupt void Port_1(void)
__interrupt关键字表明这是一个中断服 务函数,CPU见到这个关键字以后就会去做中断之前的准备工作。Port_1是用户自己取的函数名称,这个名称可以任意命名。

下图为中断使能寄存器 P1IE、P2IE,0 表示禁用中断,1 表示启用中断
在这里插入图片描述

下图为中断边缘选择寄存器 P1IES、P2IES,0 表示 PxIFGx 标志设置为低到高的转换,1 表示 PxIFGx 标志从高到低的转换
在这里插入图片描述

下图为中断标志寄存器 P1IFG、P2IFG,0 表示没有待处理的中断,1 表示有中断待处理
在这里插入图片描述

定时器

MSP430F5529共有两类共4个定时器,分别是Timer_A定时器3个和Timer_B定时器1个,按照每个寄存器配备的捕获/比较器的个数分别命名为Timer0_A(内有5个捕获比较器)、Timer1_A(3个)、Timer2_A(3个)、Timer0_B(7个)。

在MSP430系列定时器的命名规则中,Timer后面的数字表示定时器的编号;而下划线后面的A或B表示定时器的类型;A后面的数字3表示定时器中包含3个比较/捕获单元。

Timer_A定时器是带有中断功能的,当定时器计数值到达设定的值时,将自动产生一个定时器中断信号。我们可以利用这个功能方便地制造一个周期性的中断,这样就可以每隔一段事件执行一个指令。利用这个中断也可以进行延时,并且这样的延时是不需要消耗CPU资源的。

Timer_A定时器除了完成计数以外,还“附赠”了一个重要的功能模块,叫做比较/捕获单元。顾名思义,这个单元有捕获和比较这两个功能。捕获功能的作用是可以自动捕捉一个外部信号的电平变化,并记录下该变化发生的时刻。例如一个周期性的方波,可以通过捕获单元分别记录两次上升沿(电平由低变高)或是下降沿(电平由高变低)的时刻,并将二者相减来计算出波形的周期。而比较功能最大的作用是可以自动产生脉宽调制波形(PWM),目前各种数字控制信号中很多都是通过PWM波来实现的,利用比较功能可以在不消耗CPU资源的情况下产生多路PWM波形。但需要注意的是,在同一时刻下,一个比较/捕获单元中只能在比较和捕获功能中选择一个进行使用,两个功能是无法同时使用的。
在这里插入图片描述
Timer_A的核心是一个16位的计数器,其最大计数值是2的16次方,即从0到65535之间计数。在每个定时器的时钟周期,计数器的值会自动加1(或减1,取决于计数模式),我们可以通过配置寄存器来选择定时器的时钟周期。

计数器的计数值存在一个名为TAR的寄存器当中。默认状态下,当计数器的值达到65535之后,计数器会自动回到0并重新开始计数——这叫做计数器溢出。当计数器溢出时,定时器可以产生一个溢出中断,Timer_A的溢出中断标志位TAIFG会被置为1。这时如果我们开启了定时器中断使能位TAIE以及全局中断使能位,则MSP430会自动进入到定时器溢出中断服务函数中。

计数器的工作模式

通过TACTL寄存器中的MCx位可以配置,其中MCx=00为停止,另外3种模式分别是:连续计数模式(continuousmode)、向上计数模式(up mode)、向上/向下计数模式(up/downmode)。

连续计数模式:设置MCx=10,计数器将工作在连续计数模式下。在此模式下,TAR寄存器将从0到65535连续增加,到65535后则清零重新计数。在连续计数模式下,定时器的周期仅由时钟源的频率决定,频率越高,则越快计数到65535,定时器周期越短。

向上计数模式:设置MCx=01,计数器将工作在向上计数模式。与连续计数模式不同的是,向上计数模式时计数器的溢出值是由TACCR0寄存器设定的,计数器达到设定值后将自动清零。例如将TACCR0设为40959,则TAR的值只能在0到40959之间计数。在向上计数模式下,定时器周期不仅与时钟源有关,还与TACCR0的设定值有关。

向上/向下计数模式:设置MCx=11,计数器将工作在向上/向下计数模式。与向上计数不同的是,当计数器到达TACCR0的设定值之后,计数器不清零,而是从递增变成递减,直到计数器的值回到0。在向上/向下计数模式下,定时器的周期是向上计数模式的两倍。

从上述3中工作模式的描述中可以看到,计数器工作时有2个关键节点,分别是计数达到CCR0和65535。与之对应的,定时器可以产生2个中断,分别是CCR0中断和溢出中断,它们对应的中断标志位分别是CC0IFG和TA0IFG。因为Timer_A定时器有3个比较/捕获单元,所以其实还可以产生CCR1和CCR2中断。

1、CCR0中断

当计数器的值达到CCR0时,定时器会产生CCR0中断。CCR0中断是定时器所有中断中优先级最高的,并且单独拥有一个中断向量。而CCR1和CCR2与定时器溢出中断共用一个中断向量。CCR0中断的使能位CCIE和标志位CCIFG都在TACCTL0寄存器中,其中断向量名是TIMER0_A0_VECTOR。

要使用CCR0中断,首先要使能CCIE使能位,然后最关键的一步就是设置CCR0寄存器的值。无论是在向上计数模式还是向上/向下计数模式,当计数器的值到达CCR0时CCIFG都会被置位。当进入CCR0的中断服 务函数以后,CCIFG标志位会自动复位,不需要手动设置。

下面是一个CCR0中断的示例程序:

int main(void)
{
    ...
    TACCTL0 = CCIE;                                        // CCR0 中断使能
    TACCR0 = 1000;                                         // 设置 CCR0 值
    ...
}

// Timer A0 CCR0 interrupt ser-vice routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A(void)
{
    ...
}

TACCTL0 寄存器:Timer_A 捕获/比较控制 0,控制 CCR0 中断的使能

TACCR0 寄存器:Timer_A 捕获/比较 0,设置 CCR0 的值

2、定时器溢出中断

除了CCR0中断之外,计数器还可以产生一个溢出中断。需要注意的是溢出中断发生的时刻并不是TAR的计数值等于溢出值的时刻,而是TAR的值返回到0的时刻。例如在向上计数模式下,CCR0中断信号产生的时刻是TAR等于CCR0的时刻,而溢出中断产生的时刻是TAR溢出后返回0的时刻,二者相差了一个时钟周期。

溢出中断的使能位TAIE和标志位TAIFG都在TACTL寄存器中。溢出中断与CCR1、CCR2中断共同分享一个中断向量,向量名是TIMER0_A1_VECTOR。那么如果同时开启了溢出中断和CCR1/CCR2中断,如何区分究竟是哪一个中断触发了中断向量呢?在定时器中有一个寄存器TAIV,它的第1-3位包含了中断源的信息。在中断服务函数中读取TAIV寄存器的值可以判断究竟是哪一个中断信号触发了中断。
在这里插入图片描述
对TAIV的读/写操作都会使当前优先级最高的一个中断标志位自动复位。如果此时还有其他中断在等待,那么该中断会在当前中断结束后执行。例如当前TACCR1和TACCR2标志位都被置位,那么读写TAIV之后,TACCR1的标志位会被自动复位,但TACCR2不会。当前中断服务函数执行完之后会直接进入TACCR2的中断。

下面是一个定时器溢出中断的示例程序。与CCR0中断相比,主要是多了读取TAIV来判断中断源的语句。

int main(void)
{
    ...
    TACTL = TASSEL_2 + MC_2 + TAIE;                // 时钟, 控制模式, 中断
    ...
}

// Timer_A interrupt vector (TA0IV) handler
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A(void)
{
    switch( TA0IV )		// 用来判断是 CCR1、CCR2、溢出中断三种中的哪一种中断
    {
        case  2: break;                          // CCR1 not used
        case  4: break;                          // CCR2 not used
        case 10: P1OUT |= 0x01;           // overflow	
        break;
    }
}

Timer_A 计数器的使用步骤

  1. 选择定时器的时钟来源(TASSELx)及分频值(IDx),常用的时钟来源有ACLK和SMCLK,此外也可以用外部时钟输入到TACLK引脚作为时钟源
  2. 确定定时器工作模式(MCx)
  3. 根据定时器工作模式确定使用哪个中断,并进行初始化。如果是CCR0中断需要打开使能位并设置CCR0的值;如果是溢出中断只要打开使能位即可
  4. 编写对应的中断服务函数

TASSELx(Timer_A 时钟源选择)

TASSELx时钟
00TACLK
01ACLK
10SMCLK
11INCLK(INCLK 是特定于设备的,通常分配给反转的 TBCLK)(请参阅特定于设备的数据表)

MCx(确定定时器的工作模式),即连续计数、向上计数、向上/向下计数

MCx模式
00停止模式:定时器停止。
01向上模式:定时器计数到 TACCR0
10连续模式:定时器计数到 0FFFFh
11向上/向下模式:定时器向上计数到 TACCR0,然后向下计数到 0000h

下图为TACTL(Timer_A 控制寄存器)的详细参数
在这里插入图片描述

系统时钟的设置如下图所示
在这里插入图片描述

PWM输出

注意:比较模块产生的PWM波只能在特定引脚输出,且CCRx寄存器与TAx引脚一一对应。例如Timer_A0CCR1产生的PWM只能在TA0.1,Timer_A0 CCR2产生的PWM只能在TA0.2,以此类推。需要注意的是在比较模块中,CCR0是用来设定周期的,因此一个定时器最多可以产生2路不同的PWM波形。

操作步骤

  1. 先配置 PWM 的输出引脚(在 MSP430 中,这些芯片用 TA0.x 或 TA1.x 来标明。TA0.0-TA0.4 是 Timer0 的输出端口,TA1.0-TA1.2 是 Timer1 的输出端口,TA2.0-TA2.2 是 Timer2 的输出端口,但是由于CCR0 在比较模式下不能用来输出 PWM,所以极限情况下一共可以输出 8 路 PWM 波)
  2. 设定 CCR0 和 CCRx 的值(周期由 CCR0 寄存器决定,CCR0 的值乘以定时器时钟周期就是 PWM 的周期,占空比 = CCRx/CCR0,在输出模式 7 下,CCRx 的值与占空比成正比)
  3. 选择比较/捕获模块的输出模式(我们一般选择输出模式 7 来生成 PWM 波,输出模式通过 TACCTLx 寄存器来设置,例如要选择 TA1.1 引脚的输出模式,就需要配置 TA1CCTL1 寄存器)
  4. 选择定时器的时钟源和计数模式(定时器的时钟源也决定了PWM的周期。Timer_A 定时器可以使用 SMCLK 或者 ACLK,并且可以分频。时钟和分频都由 TACTL 寄存器设定。另外这个寄存器也负责选择计数器的计数模式,我们一般选择向上计数模式)
  5. MSP430开始自动生成PWM波,CPU可进入低功耗模式(设置好上述步骤以后,比较模块就将开始生成PWM波。由于这样产生的PWM波完全是由定时器生成的,与CPU无关,因此现在CPU可以去处理更重要的任务,或是进入低功耗模式休息)

PWM输出程序

#include "msp430f5529.h"

int main( void )
{
  // Stop watchdog timer to prevent time out reset
  WDTCTL = WDTPW + WDTHOLD;


  P2DIR |= BIT0;        // Set P2.0 as TA1.1
  P2SEL |= BIT0;        // SEL寄存器:配置IO口功能

  
  TA1CCR0 = 21000;        // CCR0 设置 PWM 周期    20ms
  TA1CCR1 = 2625;        // CCRx 设置 PWM 占空比 0.5ms-2.5ms 525-2625

  TA1CCTL1 = OUTMOD_7;  // 设置 TACCTLx 寄存器的输出模式为模式 7
  
  // 设定好 TACTL 寄存器之后,定时器就开始工作了,因此一般将设置TACTL的指令放在最后
  TA1CTL = TASSEL_2 + MC_1;     // 选择定时器的时钟源为 SMCLK,计数模式为向上计数模式 注意!!!!这里 SMCLK 频率应该是1.05MHz!!!!
                                // 上面时钟频率的参考  
  _BIS_SR(CPUOFF);      // Enter LMP0
  
  
}

上述时钟 SMCLK 时钟频率为 1.05MHz 请转至链接

想了解更多时钟频率请参考该链接

串行通信

MSP430的串行通信外设叫做USCI(universal serialcommunication interface)。USCI又分为两个版本,USCI_A和USCI_B。USCI_A可配置为UART、LIN、IrDA、SPI模式。USCI_B可配置为SPI和I2C模式。
用USCI模块实现UART其实很简单,只要配置好初始化参数之后,将要发送的数据写入发送缓冲器TXBUF,或是从接收缓冲期RXBUF中读取收到的数据即可。

USCI_A UART 的配置流程

MSP430单片机上电以后,单片机会自动将USCI的复位控制位UCSWRST置为1。这个操作将自动将USCI中断相关的寄存器(包括UCAxRXIE、UCAxTXIE、UCAxRXIFG、UCRXERR、UCBRK、UCPE、UCOE、UCFE、UCSTOE及UCBTOE)复位。我们在使用USCI模块前要手动将UCSWRST置0,然后USCI模块才能正常工作。对USCI进行配置的推荐流程如下:

  1. 令 UCSWRST = 1,对 USCI 模块进行复位(先置 1 使 USCI 模块复位,然后在置 0 使 USCI 模块正常工作)
  2. 改写相关寄存器(UCAxCTL0、UCAxCTL1等),对 USCI 进行初始化
  3. 配置相关I/O口
  4. 令 UCSWRST = 0
  5. 配置 UCAxRXIE 和 UCAxTXIE 寄存器,使能 USCI 中断

在这里插入图片描述

注意引脚要用跳线帽短接
在这里插入图片描述

#include <msp430f5529.h>

void delay(int ms)        // 延时函数
{
	for(int i=0;i<ms;i++)
	for(int j=0;j<240;j++);
}

void send_buf(unsigned char *ptr)    // 发送函数
{
	while(*ptr != '\0')
	{
		while(!(UCA1IFG & UCTXIFG));
		UCA1TXBUF = *ptr;
		ptr++;
		delay(50);
	}
}

void main(void)
{
	WDTCTL = WDTPW + WDTHOLD;      // 关闭看门狗
	
	P4DIR |= BIT7;          // 初始化 LED1

	//串口初始化
	P4SEL    |=  BIT4+BIT5 ;        // 设置 P4.4/4.5 的引脚模式
    	UCA1CTL1 |=  UCSWRST;           // 将状态机置于复位状态(复位寄存器后再手动开启)
   	UCA1CTL1 |=  UCSSEL_1;          // 选择 ACLK 时钟
        
        // 设置波特率
   	UCA1BR0   =  0x03;              // 32768Hz 9600 波特率为 9600
   	UCA1BR1   =  0x00;              // 32768Hz 9600
        
        // UCAxMCTL 调制控制寄存器
   	UCA1MCTL |=  UCBRS_3 + UCBRF_0; // Modulation UCBRSx=1, UCBRFx=0
    	UCA1CTL1 &= ~UCSWRST;           // **Initialize USCI state machine**
   	UCA1IE   |=  UCRXIE;            // 使能中断
         
        // 按键点灯
	P2IE |= BIT1;
	P2IES |= BIT1;
	P2IFG &= ~BIT1;
	P2REN |= BIT1;
	P2OUT |= BIT1;
	_EINT();                //中断使能

	
	while(1)
	{
            send_buf("串口通信\r\n");	
	}
}

#pragma vector = USCI_A1_VECTOR
__interrupt void USCI_ISR()
{
	switch(__even_in_range(UCA1IV,4))
	{
	case 0:break;   // Vector 0 - No interrupt
    	case 2:         // Vector 2 - RXIFG
           UCA1TXBUF = UCA1RXBUF;       // 发送接收到的数据
                                        // 如需对发送信息进行处理修改此处
        	break;
   	 case 4:break;                  // Vector 4 - TXIFG
   	 default: break;
	}
}

#pragma vector = PORT2_VECTOR
__interrupt void P2_ISR()
{
	if(P2IFG & BIT1)
	{
		while((P2IN & BIT1)==0);
		P4OUT ^= BIT7;
		send_buf("Pressed\r\n");
	}
	P2IFG &=~ BIT1;
}

OELD显示(四针IIC)

参考链接


//  P3.1 Clock (UCB0SCL)
//      
//  P3.0 Data (UCB0SDA)

#include <msp430.h> 
#include <stdint.h>
#include <stdbool.h>
#include "oledfont.h"

//******************************************************************************
// Example Commands ************************************************************
//******************************************************************************
#define X_WIDTH 	128
#define Y_WIDTH 	64
#define OLED_CMD  0	//写命令
#define OLED_DATA 1	//写数据
#define SLAVE_ADDR  0x3C

/* CMD_TYPE_X_SLAVE are example commands the master sends to the slave.
 * The slave will send example SlaveTypeX buffers in response.
 *
 * CMD_TYPE_X_MASTER are example commands the master sends to the slave.
 * The slave will initialize itself to receive MasterTypeX example buffers.
 * */

#define CMD_TYPE_0_SLAVE      0
#define CMD_TYPE_1_SLAVE      1
#define CMD_TYPE_2_SLAVE      2

#define CMD_TYPE_0_MASTER      3
#define CMD_TYPE_1_MASTER      4
#define CMD_TYPE_2_MASTER      5

#define TYPE_0_LENGTH   1
#define TYPE_1_LENGTH   2
#define TYPE_2_LENGTH   6

#define MAX_BUFFER_SIZE     20

/* MasterTypeX are example buffers initialized in the master, they will be
 * sent by the master to the slave.
 * SlaveTypeX are example buffers initialized in the slave, they will be
 * sent by the slave to the master.
 * */

uint8_t MasterType2 [TYPE_2_LENGTH] = {'F', '4', '1', '9', '2', 'B'};
uint8_t MasterType1 [28] = {0xae,0x00,0x10,0x40,0x81,0xcf,0xa1,0xc8,0xa6,0xa8,0x3f,0xd3,0x00,0xd5,0x80,0xd9,0xf1,0xda,0x12,0xdb,0x40,0x20,0x02,0x8d,0x14,0xa4,0xa6,0xaf};
uint8_t MasterType0 [1] = {0};


uint8_t SlaveType2 [TYPE_2_LENGTH] = {0};
uint8_t SlaveType1 [TYPE_1_LENGTH] = {0};
uint8_t SlaveType0 [TYPE_0_LENGTH] = {0};

void delay(unsigned int z)
{
  unsigned int x,y;
  for(x=z;x>0;x--)
    for(y=5000;y>0;y--);
}

//******************************************************************************
// General I2C State Machine ***************************************************
//******************************************************************************

typedef enum I2C_ModeEnum{
    IDLE_MODE,
    NACK_MODE,
    TX_REG_ADDRESS_MODE,
    RX_REG_ADDRESS_MODE,
    TX_DATA_MODE,
    RX_DATA_MODE,
    SWITCH_TO_RX_MODE,
    SWITHC_TO_TX_MODE,
    TIMEOUT_MODE
} I2C_Mode;

/* Used to track the state of the software state machine*/
I2C_Mode MasterMode = IDLE_MODE;

/* The Register Address/Command to use*/
uint8_t TransmitRegAddr = 0;

/* ReceiveBuffer: Buffer used to receive data in the ISR
 * RXByteCtr: Number of bytes left to receive
 * ReceiveIndex: The index of the next byte to be received in ReceiveBuffer
 * TransmitBuffer: Buffer used to transmit data in the ISR
 * TXByteCtr: Number of bytes left to transfer
 * TransmitIndex: The index of the next byte to be transmitted in TransmitBuffer
 * */
uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0};
uint8_t RXByteCtr = 0;
uint8_t ReceiveIndex = 0;
uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0};
uint8_t TXByteCtr = 0;
uint8_t TransmitIndex = 0;

/* I2C Write and Read Functions */

/* For slave device with dev_addr, writes the data specified in *reg_data
 *
 * dev_addr: The slave device address.
 *           Example: SLAVE_ADDR
 * reg_addr: The register or command to send to the slave.
 *           Example: CMD_TYPE_0_MASTER
 * *reg_data: The buffer to write
 *           Example: MasterType0
 * count: The length of *reg_data
 *           Example: TYPE_0_LENGTH
 *  */
I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count);

/* For slave device with dev_addr, read the data specified in slaves reg_addr.
 * The received data is available in ReceiveBuffer
 *
 * dev_addr: The slave device address.
 *           Example: SLAVE_ADDR
 * reg_addr: The register or command to send to the slave.
 *           Example: CMD_TYPE_0_SLAVE
 * count: The length of data to read
 *           Example: TYPE_0_LENGTH
 *  */
I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count);
void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count);

I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count)
{
    /* Initialize state machine */
    MasterMode = TX_REG_ADDRESS_MODE;
    TransmitRegAddr = reg_addr;
    RXByteCtr = count;
    TXByteCtr = 0;
    ReceiveIndex = 0;
    TransmitIndex = 0;

    /* Initialize slave address and interrupts */
    UCB0I2CSA = dev_addr;
    UCB0IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
    UCB0IE &= ~UCRXIE;                       // Disable RX interrupt
    UCB0IE |= UCTXIE;                        // Enable TX interrupt

    UCB0CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
    __bis_SR_register(LPM0_bits + GIE);              // Enter LPM0 w/ interrupts

    return MasterMode;

}


I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count)
{
    /* Initialize state machine */
    MasterMode = TX_REG_ADDRESS_MODE;
    TransmitRegAddr = reg_addr;

    //Copy register data to TransmitBuffer
    CopyArray(reg_data, TransmitBuffer, count);

    TXByteCtr = count;
    RXByteCtr = 0;
    ReceiveIndex = 0;
    TransmitIndex = 0;

    /* Initialize slave address and interrupts */
    UCB0I2CSA = dev_addr;
    UCB0IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
    UCB0IE &= ~UCRXIE;                       // Disable RX interrupt
    UCB0IE |= UCTXIE;                        // Enable TX interrupt

    UCB0CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
    __bis_SR_register(LPM0_bits + GIE);              // Enter LPM0 w/ interrupts

    return MasterMode;
}

void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count)
{
    uint8_t copyIndex = 0;
    for (copyIndex = 0; copyIndex < count; copyIndex++)
    {
        dest[copyIndex] = source[copyIndex];
    }
}

//******************************************************************************
// Device Initialization *******************************************************
//******************************************************************************

void initClockTo16MHz()
{
    UCSCTL3 |= SELREF_2;                      // Set DCO FLL reference = REFO
    UCSCTL4 |= SELA_2;                        // Set ACLK = REFO
    __bis_SR_register(SCG0);                  // Disable the FLL control loop
    UCSCTL0 = 0x0000;                         // Set lowest possible DCOx, MODx
    UCSCTL1 = DCORSEL_5;                      // Select DCO range 16MHz operation
    UCSCTL2 = FLLD_0 + 487;                   // Set DCO Multiplier for 16MHz
                                              // (N + 1) * FLLRef = Fdco
                                              // (487 + 1) * 32768 = 16MHz
                                              // Set FLL Div = fDCOCLK
    __bic_SR_register(SCG0);                  // Enable the FLL control loop

    // Worst-case settling time for the DCO when the DCO range bits have been
    // changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
    // UG for optimization.
    // 32 x 32 x 16 MHz / 32,768 Hz = 500000 = MCLK cycles for DCO to settle
    __delay_cycles(500000);//
    // Loop until XT1,XT2 & DCO fault flag is cleared
    do
    {
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // Clear XT2,XT1,DCO fault flags
        SFRIFG1 &= ~OFIFG;                          // Clear fault flags
    }while (SFRIFG1&OFIFG);                         // Test oscillator fault flag
}


void initGPIO()
{
    //I2C Pins
    P3SEL |= BIT0 + BIT1;                     // P3.0,1 option select

}

void initI2C()
{
    UCB0CTL1 |= UCSWRST;                      // Enable SW reset(复位使能)
    UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     //  Master, I2C,synchronous mode(同步模式)
    UCB0CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
    UCB0BR0 = 160;                            // fSCL = SMCLK/160 = ~100kHz
    UCB0BR1 = 0;
    UCB0I2CSA = SLAVE_ADDR;                   // Slave Address is 048h
    UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
    UCB0IE |= UCNACKIE;                       //使能中断
}

void OLED_WrCmd(unsigned char IIC_Command)
{
        MasterType0[0]=IIC_Command;
      I2C_Master_WriteReg(SLAVE_ADDR, 0x00,MasterType0, 1);
}

void OLED_WrDat(unsigned char IIC_Data)
{
	MasterType0[0]=IIC_Data;
      I2C_Master_WriteReg(SLAVE_ADDR, 0x40,MasterType0, 1);
}

//******************************************************************************
// Main ************************************************************************
// Send and receive three messages containing the example commands *************
//******************************************************************************

void OLED_Fill(unsigned char bmp_dat) 
{
	unsigned char y,x;
	for(y=0;y<8;y++)
	{
		OLED_WrCmd(0xb0+y);
		OLED_WrCmd(0x01);
		OLED_WrCmd(0x10);
		for(x=0;x<128;x++)
		OLED_WrDat(bmp_dat);
	}
}

void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 
	OLED_WrCmd(0xb0+y);
	OLED_WrCmd(((x&0xf0)>>4)|0x10);
	OLED_WrCmd((x&0x0f)|0x01);
} 

void OLED_Init(void)
{
	unsigned char i=0;
        for(;i<28;i++)
        {
          MasterType0[0]=MasterType1[i];
          I2C_Master_WriteReg(SLAVE_ADDR, 0x00,MasterType0, 1);
        }
	OLED_Fill(0x00); //初始清屏
	OLED_Set_Pos(0,0);
}

void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
	if(cmd)
			{

   OLED_WrDat(dat);
   
		}
	else {
   OLED_WrCmd(dat);
		
	}
}


void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char Char_Size)
{    	
	unsigned char c=0,i=0;	
		c=chr-' ';//得到偏移后的值			
		if(x>128-1){x=0;y=y+2;}
		if(Char_Size ==16)
			{
			OLED_Set_Pos(x,y);	
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
			OLED_Set_Pos(x,y+1);
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
			}
			else {	
				OLED_Set_Pos(x,y);
				for(i=0;i<6;i++)
				OLED_WR_Byte(F6x8[c][i],OLED_DATA);
				
			}
}

void OLED_U32toU16(unsigned char x,unsigned char y ,unsigned int n,unsigned char k)
{
	switch(n)
	{
		case 0 :OLED_ShowChar(x,y,'0',k);break;
		case 1 :OLED_ShowChar(x,y,'1',k);break;
		case 2 :OLED_ShowChar(x,y,'2',k);break;
		case 3 :OLED_ShowChar(x,y,'3',k);break;
		case 4 :OLED_ShowChar(x,y,'4',k);break;
		case 5 :OLED_ShowChar(x,y,'5',k);break;
		case 6 :OLED_ShowChar(x,y,'6',k);break;
		case 7 :OLED_ShowChar(x,y,'7',k);break;
		case 8 :OLED_ShowChar(x,y,'8',k);break;
		case 9 :OLED_ShowChar(x,y,'9',k);break;
	}
}

void OLED_Show_Number(unsigned char x,unsigned char y ,unsigned int a,unsigned char n)
{
	unsigned int b,c,d,e,f,g;
	b=a/100000;
	c=a%100000/10000;
	d=a%10000/1000;
	e=a%1000/100;
	f=a%100/10;
	g=a%10;
	if(b!=0)
	{
		OLED_U32toU16(x,y,b,n);
		OLED_U32toU16(x+n/2,y,c,n);
		OLED_U32toU16(x+n,y,d,n);
		OLED_U32toU16(x+(n/2)*3,y,e,n);
		OLED_U32toU16(x+2*n,y,f,n);
		OLED_U32toU16(x+(n/2)*5,y,g,n);
	}else if(b==0&&c!=0)
	{
		OLED_U32toU16(x,y,c,n);
		OLED_U32toU16(x+n/2,y,d,n);
		OLED_U32toU16(x+n,y,e,n);
		OLED_U32toU16(x+(n/2)*3,y,f,n);
		OLED_U32toU16(x+2*n,y,g,n);
	}else if(b==0&&c==0&&d!=0)
	{
		OLED_U32toU16(x,y,d,n);
		OLED_U32toU16(x+n/2,y,e,n);
		OLED_U32toU16(x+n,y,f,n);
		OLED_U32toU16(x+(n/2)*3,y,g,n);
	}else if(b==0&&c==0&&d==0&&e!=0)
	{
		OLED_U32toU16(x,y,e,n);
		OLED_U32toU16(x+n/2,y,f,n);
		OLED_U32toU16(x+n,y,g,n);
	}else if(b==0&&c==0&&d==0&&e==0&&f!=0)
	{
		OLED_U32toU16(x,y,f,n);
		OLED_U32toU16(x+n/2,y,g,n);
	}else
	{
		OLED_U32toU16(x,y,g,n);
	}
}

int main(void) {

    WDTCTL = WDTPW | WDTHOLD;                 // Stop watchdog timer

    initClockTo16MHz();                        //配置系统时钟为16Mhz
    initGPIO();
    initI2C();
    delay(500);
    OLED_Init();
    OLED_ShowChar(30, 0 , 'a', 16);
    OLED_ShowChar(40, 0 , 'b', 16);
    OLED_ShowChar(50, 0 , 'c', 16);
    OLED_Show_Number(0, 0, 666, 16);
    
    __bis_SR_register(LPM0_bits + GIE);
}

//******************************************************************************
// I2C Interrupt ***************************************************************
//******************************************************************************

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
  //Must read from UCB0RXBUF
  uint8_t rx_val = 0;

  switch(__even_in_range(UCB0IV,0xC))
  {
    case USCI_NONE:break;                             // Vector 0 - no interrupt
    case USCI_I2C_UCALIFG:break;                      // Interrupt Vector: I2C Mode: UCALIFG
    case USCI_I2C_UCNACKIFG:break;                    // Interrupt Vector: I2C Mode: UCNACKIFG
    case USCI_I2C_UCSTTIFG:break;                     // Interrupt Vector: I2C Mode: UCSTTIFG
    case USCI_I2C_UCSTPIFG:break;                     // Interrupt Vector: I2C Mode: UCSTPIFG
    case USCI_I2C_UCRXIFG:
        rx_val = UCB0RXBUF;
        if (RXByteCtr)
        {
          ReceiveBuffer[ReceiveIndex++] = rx_val;
          RXByteCtr--;
        }

        if (RXByteCtr == 1)
        {
          UCB0CTL1 |= UCTXSTP;
        }
        else if (RXByteCtr == 0)
        {
          UCB0IE &= ~UCRXIE;
          MasterMode = IDLE_MODE;
          __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
        }
        break;                      // Interrupt Vector: I2C Mode: UCRXIFG
    case USCI_I2C_UCTXIFG:
        switch (MasterMode)
        {
          case TX_REG_ADDRESS_MODE:
              UCB0TXBUF = TransmitRegAddr;
              if (RXByteCtr)
                  MasterMode = SWITCH_TO_RX_MODE;   // Need to start receiving now
              else
                  MasterMode = TX_DATA_MODE;        // Continue to transmission with the data in Transmit Buffer
              break;

          case SWITCH_TO_RX_MODE:
              UCB0IE |= UCRXIE;              // Enable RX interrupt
              UCB0IE &= ~UCTXIE;             // Disable TX interrupt
              UCB0CTL1 &= ~UCTR;            // Switch to receiver
              MasterMode = RX_DATA_MODE;    // State state is to receive data
              UCB0CTL1 |= UCTXSTT;          // Send repeated start
              if (RXByteCtr == 1)
              {
                  //Must send stop since this is the N-1 byte
                  while((UCB0CTL1 & UCTXSTT));
                  UCB0CTL1 |= UCTXSTP;      // Send stop condition
              }
              break;

          case TX_DATA_MODE:
              if (TXByteCtr)
              {
                  UCB0TXBUF = TransmitBuffer[TransmitIndex++];
                  TXByteCtr--;
              }
              else
              {
                  //Done with transmission
                  UCB0CTL1 |= UCTXSTP;     // Send stop condition
                  MasterMode = IDLE_MODE;
                  UCB0IE &= ~UCTXIE;                       // disable TX interrupt
                  __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
              }
              break;

          default:
              __no_operation();
              break;
        }
        break;                      // Interrupt Vector: I2C Mode: UCTXIFG
    default: break;
  }
}

  • 32
    点赞
  • 212
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忱铭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值