~~首先吐槽一下CSDN的MarkDown编辑器,实在是不习惯,又改回来了……人家说我没文化,MarkDown本来就是纯阅读没那么多花里胡哨的,好吧那我就是喜欢花里胡哨
————————————————————————————————————————————————————————————————————————————
利用MSP430F5438A进行SD卡初始化
手头材料:
- MSP430F5438A开发板一个
- SD卡开发板一个
- 8GB SDHC卡一张
首先,回顾一下在理论解说(文章链接)中提到的几个注意事项:
1. 与SD卡建立SPI通信的时钟频率在20MHz~25MHz之间
那么在建立SPI通信的代码当中首先要确保这个时钟频率。
原有的例程代码当中,配置了P3.1,P3.2和P3.3组成的一个SPI通信线路,控制这个SPI端口的寄存器主要有:
UCB0CTL0和UCB0CTL1
UCB0CTL0
7-6分别是
UCCKPH和UCCKPL
这两个合在一起应该是决定模式的,采用模式0应该这两位均保持默认的00即可
5是
UCMSB
控制移位寄存器的控制位,0表示LSB first而1表示MSB first,在说明文档中并没有提到应该采用哪一种方式,但是其中附带的SPI说明链接说采用MSB在前的传输方式,推荐置位
4是
UC7BIT
0为8位数据格式,1为7位,貌似是一个和串口定义差不多的控制位,仍然在主要的说明文档中找不到描述而是在上述附带的说明链接中看出是一个8位寄存器,那么这位复位清零
3是
UCMST
决定了芯片是从属模式-0还是主动模式-1,但是主从模式的区别是什么(主机提供时钟频率)
2-1是
UCMODEx
两个合起来决定了USCI mode,这两位当第0位的UCSYNC=1是,即同步通信模式时起作用,决定通信模式究竟是一下哪一种:
00-3 pin SPI
01-4 pin SPI 从节点高电平有效
10-4 pin SPI 从节点低电平有效
11- I2C 模式
说明文档中说SD卡的通信是一个3-pin模式的SPI,片选信号用通用IO
0位上面有过介绍,1为同步模式,0为异步模式
UCB0CTL1
只有7、6、0三位有用
7-6
UCSSELx
在Master Mode中,选择BRCLK用(TI的User’s Guide说的不准确,实际上无论是BRCLK还是其他时钟源,都是要进一步分频的),Slave Mode的情况下没有用
00-保留
01-ACLK
10-SMCLK
11-SMCLK
0
UCSWRST
默认为1,
0-Disabled,USCI 复位释放以供操作
1-Enabled,USCI在该复位状态下“逻辑锁定”(logic held)
通常在设置SPI接口属性的时候将该位置位
那么这部分的代码是
void Init_SPI(void)
{
P8OUT |= LCD_NCS ; // SPI端口复用,DisableTFT液晶端口
P8DIR |= LCD_NCS ;
P3OUT |= NCS25 ;
P3DIR |= NCS25 ;
P3SEL &= 0xF0 ;
P3SEL |= 0x0E ; // // P3.1/2/3功能选择为SPI
UCB0CTL1 |= UCSWRST ; // 复位SPI状态机
UCB0CTL0 |= UCMST+UCSYNC+UCCKPL+UCMSB ; // 3-pin, 8-bit SPI master, Clock polarity high, MSB
UCB0CTL1 |= UCSSEL_2 ; // 选择SCK参考源为SMCLK
UCB0BR0 = 0x02 ; // SCK = SMCK/2
UCB0BR1 = 0 ;
UCB0CTL1 &=~UCSWRST ; // SPI状态机使能
}
可以看到后续还配置了
UCB0BR0 和 UCB0BR1
这两个是预分频系数,但是分频完了之后给谁了?实际上分频之后才是真正的SPI时钟频率
但是这个例程的注释写的似乎有问题,如果将UCB0BR0设为2而UCB0BR1设为0,似乎应该是3分频
根据这个代码目前的配置情况来看:
SMCLK的值很重要
那么例程对时钟的配置如下:
void Init_CLK(void)
{
WDTCTL = WDTPW + WDTHOLD ; // 关看门狗
P7SEL |= 0x03 ; // 端口选择外部低频晶振XT1
UCSCTL6 &=~XT1OFF ; // 使能外部晶振
UCSCTL6 |= XCAP_3 ; // 设置内部负载电容
UCSCTL3 |= SELREF_2 ; // DCOref = REFO
UCSCTL4 |= SELA_0 ; // ACLK = XT1
__bis_SR_register(SCG0) ; // 关闭FLL控制回路
UCSCTL0 = 0x0000 ; // 设置DCOx, MODx
UCSCTL1 = DCORSEL_7 ; // 设置DCO振荡范围
UCSCTL2 = FLLD__1 + FLL_FACTOR ; // Fdco = ( FLL_FACTOR + 1)×FLLRef = (649 + 1) * 32768 = 21.2992MHz
__bic_SR_register(SCG0) ; // 打开FLL控制回路
__delay_cycles(1024000) ;
do
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG); // 清除 XT2,XT1,DCO 错误标志
SFRIFG1 &= ~OFIFG ;
}while(SFRIFG1&OFIFG) ; // 检测振荡器错误标志
}
看来似乎其它时钟源给的都是默认的,SMCLK的默认值是分频之后的DCO,即DCOCLKDIV(见USER's GUIDE P175,UCSCTL4决定),而DCO的频率的确定方式比较复杂:
首先,确定FLL(Frequency Locked Loop)的 REFCLK(参考时钟频率):这个行为通过确定UCSCTL3中的SELREF(第6~4位)完成,默认为XT1(外置低频晶振-这里手头的开发板是32768Hz),上面代码则设为了REFO(这是一个内部的参考频率源,也是32768Hz)
然后,根据确定的FLL设置倍频和分频系数,倍频系数通过设置UCSCTL2中的FLLD(14~12,默认001,二分频)和FLLN(9~0,默认后五位为1即31)完成,分频系数则通过设置UCSCTL3当中的FLLREFDIV(2~0,默认000不分频)。以上代码中的设定:FLLD=1(FLLD__1代表将FLLD全部清0),FLLN=649(前面对FLL_FACTOR有个define,是649),FLLREFDIV代码没有管,那么直接就是默认值不分频
综上,根据5438A的User's Guide
DCO时钟频率=FLLD x (FLLN+1) x (FLLREFCLK /FLLREFDIV )
DCO时钟频率分频后 = DCO时钟平率/FLLD
换算一下也就是
Fdco = 1x(649+1)x(32768/1),也就是注释当中的那个算式,结果为21.2992MHz
DCO频率确定以后,DCOCLKDIV(分频之后的)就是这个Fdco,因为FLLD分频系数就是1
这样一个通信频率再3分频(约7.1MHz)或者即使如原注释所说的2分频(约10.5MHz)显然不足正常工作时的频率范围(20~25MHz)而又高于最开始初始化的频率100kHz~400kHz
所以,程序需要进行修改!