This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
第三章 时钟系统(CS)
3.1 本章引言
3.2 函数总览
1
|
void GS_setupDCO ( uint8_t mode )
|
使用选中的模式配置DCO
|
|
2
|
void CS_initClockSignal (uint8_t clockSource, uint8_t clockSourceDivider)
|
使用分频器初始化时钟信号
|
|
3
|
uint32_t CS_getACLK (void)
|
获取当前ACLK的频率(单位Hz)
|
|
4
|
uint32_t CS_getSMCLK (void)
|
获取当前SMCLK的频率(单位Hz)
|
|
5
|
uint32_t CS_getMCLK (void)
|
获取当前MCLK的频率(单位Hz)
|
|
6
|
uint8_t CS_getFaultFlagStatus (uint8_t mask)
|
获取DCO故障(或错误)标志状态
|
第四章 EUSCI通用异步接收器/发送器(EUSCI_A_UART)
4.1 本章引言
4.2 函数总览
1
|
bool EUSCI_A_UART_init ( uint16_t baseAddress,EUSCI_A_UART_initParam *param )
|
先进的UART模块初始化程序,把初始化参数通过初始化函数写进时钟预分频器。
|
|
2
|
void EUSCI_A_UART_transmitData (uint16_t baseAddress, uint8_t transmitData)
|
从UART模块发送出去一个字节
|
|
3
|
void EUSCI_A_UART_receiveData (uint16_t baseAddress)
|
接收一个字节数据。
|
|
4
|
void EUSCI_A_UART_enableInterrupt (uint16_t baseAddress,uint8_t mask)
|
使能UART(独立的)中断源
|
|
5
|
void EUSCI_A_UART_disableInterrupt (uint16_t baseAddress,uint8_t mask)
|
关闭UART(独立的)中断源
|
|
6
|
void EUSCI_A_UART_getInterruptStatus (uint16_t baseAddress,uint8_t mask)
|
获取当前UART中断状态
|
|
7
|
void EUSCI_A_UART_clearInterruptStatus (uint16_t baseAddress,uint8_t mask)
|
清除UART中断源。(备注:清除中断状态,重置中断状态标识)
|
|
8
|
void EUSCI_A_UART_enable (uint16_t baseAddress)
|
启用UART模块
|
|
9
|
void EUSCI_A_UART_disable (uint16_t baseAddress)
|
关闭UART模块
|
|
10
|
uint8_t EUSCI_A_UART_queryStatusFlags (uint16_t baseAddress, uint8_t mask)
|
获取当前UART状态标志.(查询UART当前状态标识)
|
|
11
|
void EUSCI_A_UART_setDormant(uint16_t baseAddress)
|
把UART模块设置在休眠模式
|
|
12
|
void EUSCI_A_UART_resetDormant(uint16_t baseAddress)
|
把UART模块从休眠模式唤醒
|
|
13
|
void EUSCI_A_UART_transmitAddress(uint16_t baseAddress,uint8_t transmitAddress)
|
根据所选的多处理器模式,传送下一个字节标记为地址
|
|
14
|
void EUSCI_A_UART_transmitBreak(uint16_t baseAddress)
|
发送终止
|
|
15
|
uint32_t EUSCI_A_UART_getReceiveBufferAddress(uint16_t baseAddress)
|
返回RX缓冲区的UART的DMA模块的地址。
|
|
16
|
uint32_t EUSCI_A_UART_getTransmitBufferAddress(uint16_t baseAddress)
|
返回TX缓冲区的UART的DMA模块的地址。
|
|
17
|
void EUSCI_A_UART_selectDeglitchTime(uint16_t baseAddress,uint16_t deglitchTime)
|
设置抗尖峰脉冲时间
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
mask
|
将被清除中断源的位掩码。掩码值可以是以下量的逻辑或:
EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG
EUSCI_A_UART_TRANSMIT_INTERRUPT_FLAG
EUSCI_A_UART_STARTBIT_INTERRUPT_FLAG
EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
mask
|
掩码位对应的中断源将会被关闭。掩码值可以是以下量的逻辑或:
EUSCI_A_UART_RECEIVE_INTERRUPT 接收中断
EUSCI_A_UART_TRANSMIT_INTERRUPT 发送中断
EUSCI_A_UART_RECEIVE_ERRONEOUSCHAR_INTERRUPT 收到错误的字符中断使能
EUSCI_A_UART_BREAKCHAR_INTERRUPT 接收间隔字符中断使能
EUSCI_A_UART_STARTBIT_INTERRUPT 起始位接收中断启用
EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT 发送完成中断使
|
void EUSCI_A_UART_enable(uint16_t baseAddress)
baseAddress
|
是EUSCI_A_UART模块的基地址
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
mask
|
掩码位对应的中断源将会被关闭。掩码值可以是以下量的逻辑或:
EUSCI_A_UART_RECEIVE_INTERRUPT 接收中断
EUSCI_A_UART_TRANSMIT_INTERRUPT 发送中断
EUSCI_A_UART_RECEIVE_ERRONEOUSCHAR_INTERRUPT 收到错误的字符中断使能
EUSCI_A_UART_BREAKCHAR_INTERRUPT 接收间隔字符中断使能
EUSCI_A_UART_STARTBIT_INTERRUPT 起始位接收中断启用
EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT 发送完成中断使
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
mask
|
返回掩码的中断标志位状态。掩码值可以是以下量的逻辑或:
EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG
EUSCI_A_UART_TRANSMIT_INTERRUPT_FLAG
EUSCI_A_UART_STARTBIT_INTERRUPT_FLAG
EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
mask
|
param是初始化结构体的指针。
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
mask
|
是将被返回的中断标志位状态掩码。
EUSCI_A_UART_LISTEN_ENABLE
EUSCI_A_UART_FRAMING_ERROR
EUSCI_A_UART_OVERRUN_ERROR
EUSCI_A_UART_BREAK_DETECT
EUSCI_A_UART_ADDRESS_RECEIVED
EUSCI_A_UART_IDLELINE
EUSCI_A_UART_BUSY
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
deglitchTime
|
抗尖峰脉冲时间可选的值有:
EUSCI_A_UART_DEGLITCH_TIME_2ns
EUSCI_A_UART_DEGLITCH_TIME_50ns
EUSCI_A_UART_DEGLITCH_TIME_100ns
EUSCI_A_UART_DEGLITCH_TIME_200ns
|
baseAddress
|
EUSCI_A_UART模块的基地址
|
baseAddress
|
EUSCI_A_UART模块的基地址
|
transmitAddress
|
被发送的下一个字节
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
baseAddress
|
是EUSCI_A_UART模块的基地址
|
transmitData
|
从UART模块将要被发送出去的数据
|
例程将展示怎样使用EUSCI_A_UART API 来初始化EUSCI_A_UART并开始发送字符。
// 使用SMCLK频率为16384000Hz配置UART模块波特率为115200
// 可以在以下网址计算器计算出配置参数:
// http://software-dl.ti.com/msp430/msp430 public sw/mcu/msp430/MSP430BaudRateConverter/index.html
EUSCI_A_UART_ initParam uartConfig = {
EUSCI_A_UART_ CLOCKSOURCE SMCLK, // SMCLK Clock Source
8, // BRDIV = 8
14, // UCxBRF = 14
34, // UCxBRS = 34
EUSCI_A_UART_ NO PARITY, // No Parity
EUSCI_A_UART_ MSB FIRST, // MSB First
EUSCI_A_UART_ ONE STOP BIT, // One stop bit
EUSCI_A_UART_ MODE, // UART mode
EUSCI_A_UART_ OVERSAMPLING_BAUDRATE_GENERATION // Oversampling Baudrate
};
WDT_hold(WDT_BASE);
// 设置DCO使用内部电阻,DCO将被配置在16.384MHz.
CS_setupDCO(CS_INTERNAL RESISTOR);
// SMCLK设置与DCO相同的速度。SMCLK = 16.384MHz
CS_initClockSignal(CS_SMCLK, CS_CLOCK DIVIDER_1);
// 设置P1.2和P1.3管脚作为UART管脚。P1.4 管脚作为LED输出
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN4);
GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4);
// 配置和使能UART外设
EUSCI_A_UART_ init(EUSCI_A0_BASE, &uartConfig);
EUSCI_A_UART_ enable(EUSCI_A0_BASE);
EUSCI_A_UART_ enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_ RECEIVE_INTERRUPT);
while(1) {
EUSCI_A_UART_ transmitData(EUSCI_A0_BASE, TXData);
//进入休眠并等待退出LPM
__bis_SR_register(LPM0_bits | GIE);
}
4.4 详解
21ic网友针对教程的学习讨论异常热烈,现将部分精彩问答及分享内容整理呈现:
A提问:
DCO旁路模式什么概念?
B回复:
DCO 全称是Digitally Controlled Oscillator(DCO),DCO一共有两种模式,内部部电阻模式(Internal Resistor Mode)和外部电阻模式(External Resistor Mode),通过 CSCTL0寄存器的DCOR位选择。注意旁路模式是通过CSCTL0寄存器的DCOBYP位置1选择的。通过datasheet了解到,外部电阻模式提供更高的时钟精度在绝对容度和温度漂移方面,原文为This mode offers higher clock accuracy in terms of absolute tolerance and temperature drift compared to internal resistor mode
钟模块有四个系统时钟信号可以使用:
ACLK:辅助时钟。当运行在DCO时,ACLK是固定在32kHz。如果设备是设置在DCO旁路模式,ACLK运行在旁路时钟频率的1/512。
MCLK:主时钟。MCLK可以被1,2,4,8或16分频。MCLK通常被CPU和系统使用。
SMCLK:子系统主时钟。SMCLK可以被1,2,4,8或16分频。SMCLK可以被各个外围模块通过软件选择使用。
SD24CLK:SD24时钟提供一个1.024MHz固定频率的时钟给Sigma-Delta ADC(SD24)。
该时钟只为SD24的请求所使用。如果SD24功能必须在DCO旁路模式下工作,那么外部时钟频率必须是16.384Mhz。
430单片机时钟模块正是因为有这些时钟源配合不同的低功耗模式保证超低功耗。这个系列ACLK时钟32k的,记得G系列好像是12k的。去年做了一种产品因为功耗要求很低,5uA,使用三年,就是选用了G系列的MCU,MCU大部分时间处于休眠状态LPM3,定时器工作在ACLK时钟下。。。
A提问:
看到例程上有这么一段程序:
CS_initClockSignal(CS_MCLK, CS_CLKOCK_DIVIDER_8);
CS_initClockSignal(CS_SMCLK, CS_CLKOCK_DIVIDER_2);
我想问下,这个MCLK和SMCLK一个8分频一个是2分频,他们在选择分频数值的时候是根据什么选择的?还有就是想问一下:8分频之后是不是比2分频之后的频率值小了?为什么MCLK的频率值要比SMCLK的频率值小呢?个人感觉应该要大一些才对……新手,可能问的幼稚了……
B回复:
MCLK:主时钟。MCLK可以被1,2,4,8或16分频。MCLK通常被CPU和系统使用。cpu执行指令的时钟。SMCLK:子系统主时钟。SMCLK可以被1,2,4,8或16分频。SMCLK可以被各个外围模块通过软件选择使用。例如定时器可以选择SMCLK时钟作为工作频率,定义一段时间。。。
没有规定两者那个大。。
8、
试着写了下发送和接收程序,接收在中断进行。
uint8_t Rcvdat[10],SendDat[10]i, i=0;
void EUSCI_A_UART_transmitData(EUSCI_A0_BASE,LED);
if(EUSCI_A_UART_receiveData (EUSCI_A0_BASE)=="LED_ON")
{
LED = 0;
}
if(EUSCI_A_UART_receiveData (EUSCI_A0_BASE)=="LED_OFF")
{
LED =1;
}
B点评:
每次只收一个字节的,我们可以这样,不是有停止位和启动位吗,先建立一个规则,把这个指令放到规则里,我们检测是否收到了一条完整的信息,在这条字符串找到关键词。然后判断。
9、
//前面初始化和例程相同
EUSCI_A_UART_ initParam uartConfig = {
EUSCI_A_UART_ CLOCKSOURCE SMCLK, // SMCLK Clock Source
8, // BRDIV = 8
14, // UCxBRF = 14
34, // UCxBRS = 34
EUSCI_A_UART_ NO PARITY, // No Parity
EUSCI_A_UART_ MSB FIRST, // MSB First
EUSCI_A_UART_ ONE STOP BIT, // One stop bit
EUSCI_A_UART_ MODE, // UART mode
EUSCI_A_UART_ OVERSAMPLING_BAUDRATE_GENERATION // Oversampling Baudrate
};
WDT_hold(WDT_BASE);
// 设置DCO使用内部电阻,DCO将被配置在16.384MHz.
CS_setupDCO(CS_INTERNAL RESISTOR);
// SMCLK设置与DCO相同的速度。SMCLK = 16.384MHz
CS_initClockSignal(CS_SMCLK, CS_CLOCK DIVIDER_1);
// 设置P1.2和P1.3管脚作为UART管脚。P1.4 管脚作为LED输出
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN4);
//GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4);
// 配置和使能UART外设
EUSCI_A_UART_ init(EUSCI_A0_BASE, &uartConfig);
EUSCI_A_UART_ enable(EUSCI_A0_BASE);
EUSCI_A_UART_ enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_ RECEIVE_INTERRUPT);
/////////////////////////////////////////////////////////
//原谅我的渣渣C语言,LED默认低电平亮
while(1) {
if(EUSCI_A_UART_receiveData (EUSCI_A0_BASE)=="LED_OFF")
{
GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4);
}
else if(EUSCI_A_UART_receiveData (EUSCI_A0_BASE)=="LED_ON")
{
GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4);
}
else if(EUSCI_A_UART_receiveData (EUSCI_A0_BASE)=="LED_TEST")
{
GPIO_getInputPinValue (GPIO_PORT_P1, GPIO_PIN4);
EUSCI_A_UART_ transmitData(EUSCI_A0_BASE, GPIO_getInputPinValue (GPIO_PORT_P1, GPIO_PIN4));
}
}
我看了一下那个单片机的技术手册,跟楼主的教程对比了一下,发现了一个问题,就是串口UART可选的时钟有多种,
那个MSP430i2xxFamilyUser'sGuide.pdf的231页的结构图上,写的是通UCSSELx寄存器可以选择,UCLK,ACLK,SMCLK,SMCLK,
其中SMCLK出现两次,我觉得第四个,当寄存器位对应11时候应该是MCLK,可是我查看了头文件。如下
#define UCSSEL__UCLK (0x0000) /* USCI 0 Clock Source: UCLK */
#define UCSSEL__ACLK (0x0040) /* USCI 0 Clock Source: ACLK */
#define UCSSEL__SMCLK (0x0080) /* USCI 0 Clock Source: SMCLK */
没有找到串行收发器使用MCLK的证据,也就是只要这两位的,高一位是1,就是成立的, 可以记作1x.
所以不用太纠结,这个直接用库函数操作就很OK了,经过查证那个手册也是没错的,只是没有写清楚。
我看大家都对空闲线多处理器协议那个不懂,我终于找到资料了,附上:
空闲线多处理器模式
在空闲线多处理器协议中(ADDR/IDLE MODE位为0),数据块被各数据块间的空闲时间分开,该空闲时间比块中数据帧之间的空闲时间要长。一帧后的空闲时间(1O个或更多个高电平位)表明新块的开始,每位的时间可直接由波特率的值(bit/s)计算,空闲线多处理器通信格式如图1所示。
图1 空闲线多处理器通信格式
(1)空闲线模式操作步骤
①接收到块起始信号后,SCI被唤醒。
②处理器识别下一个SCI中断。
③中断服务子程序将接收到的地址与接收节点的地址进行比较。
④如果CPU的地址与接收到的地址相符,则中断服务子程序清除SLEEP位,并接收块中剩余的数据。
⑤如果CPU的地址与接收到的地址不符,则SLEEP位仍保持在置位状态,直到检测到下一个数据块的开始,否则CPU都不会被SCI端口中断,继续执行主程序。
(2)块起始信号
有两种方法发送块的开始信号。
方法1:特意在前后两个数据块之间增加lO位或更多位的空闲时间。
方法2:在写SCITXBUF寄存器之前,SCI口首先将TXWAKE位(SCICTL1,位3)置1。这样就会自动发送11位的空闲时间。在这种模式中,除非必要,否则串行通信线不会空闲。在设置TXWAKE后发送地址数据前,要向SCITXBUF写入一个无关的数据,以保障能够发送空闲时间。
(3)唤醒暂时(WUT)标志
与TXWAKE位相关的是唤醒暂时(WUT)标志位,这是一个内部标志,与TXWAKE构成双缓冲。当TXSHF从SCITXBUF装载时,WUF从TXWAKE装入,TXWAKE清0。如图2所示。
图2 双缓冲的WUT和TXSIIF
(4)块的发送开始信号
在块传送过程中需要采用下列步骤发送块开始信号:
①写1到TXWAKE位。
②为发送一个块开始信号,写一个数据字(内容不重要)到SCITXBUF寄存器。当块开始信号被发送时,写入的数据字被禁止,且在块开始信号发送后被忽略。当TXSHF(发送移位寄存器)再次空闲后,SCITXBUF寄存器的内容被移位到TXSHF寄存器,TXWAKE的值被移位到WUT中,然后TXWAKE被清除。由于TXWAKE置1,在前一帧发送完停止位后,起始位、数据位和奇偶校验位被发送的11位空闲位取代。
③写一个新的地址值到SCITXBUF寄存器。
在传送开始信号时,必须先将一个无关数据写入SCITXBUF寄存器,从而使TXWAKE位的值能被移位到WUT中。由于TXSHF和WUT都是双级缓冲,在无关数据字被移位到TX-SHF寄存器后,才能再次将数据写入SCITXBUF。
(5)接收器操作
接收器的操作和SLEEP位无关,然而在检测到一个地址帧之前,接收器并不对RXRDY位和错误状态位置位,也不申请接收中断。
另外百度百科的地址线,就是讨论课程中的这个问题
地址线
地址线是用来传输地址信息用的。举个简单的例子:cpu在内存或硬盘里面寻找一个数据时,先通过地址线找到地址,然后再通过数据线将数据取出来。 如果有32根.就可以访问2的32次方的空间,也就是4GB。在地址位多处理器协议中(ADDR/IDLE MODE位为1),最后一个数据位后有一个附加位,称之为地址位。数据块的第一个帧的地址位设置为1,其他帧的地址位设置为0。地址位多处理器模式的数据传输与数据块之间的空闲周期无关(参看图在SCICCR寄存器中的位3——ADDR/IDLE MODE位)。
TXWAKE位的值被放置到地址位,在发送期间,当SCITXBUF寄存器和TXWAKE分别装载到TXSHF寄存器和WUT中时,TXWAKE清0,且WUT的值为当前帧的地址位的值.因此,发送一个地址需要完成下列操作:
●TXWAKE位置1,写适当的地址值到SCITXBUF寄存器。当地址值被送到TXSHF寄存器又被移出时,地址位的值被作为1发送。这样串行总线上其他处理器就读取这个地址。
●TXSHF和WUT加载后,向SCITXBUF和TXWAKE写入值(由于TXSHF和WUT是双缓冲的,它们能被立即写入)。
●TXWAKE位保持0,发送块中无地址的数据帧。
图 地址位多处理器通信格式
一般情况下,地址位格式应用于11个或更少字节的数据帧传输。这种格式在所有发送的数据字节中增加了一位(1代表地址帧,0代表数据帧);通常12个或更多字节的数据帧传输使用空闲线格式。
(1)地址字节
发送节点(Talker)发送信息的第一个字节是一个地址字节,所有接收节点(Listener)都读取该地址字节。只有接收数据的地址字节同接收节点的地址字节相符时,才能中断接收节点。如果接收节点的地址和接收数据的地址不符,接收节点将不会被中断,等待接收下一个地址字节。
(2)Sleep位
连接到串行总线上的所有处理器都将SCI SLEEP位置1(SCICTL1的第二位),这样只有检测到地址字节后才会被中断。当处理器读到的数据块地址与用户应用软件设置的处理器地址相符时,用户程序必须清除SLEEP位,使SCI能够在接收到每个数据字节时产生一个中断。
尽管当SLEEP位置1时接收器仍然工作,但它并不能将RXRDY、RXINT或任何接收器错误状态位置1,只有在检测到地址位且接收的帧地址位是1时才能将这些位置1。SCI本身并不能改变SLEEP位,必须由用户软件改变。
(3)识别地址位
处理器根据所使用的多处理器模式(空闲线模式或地址位模式),采用不同的方式识别地址字节,例如:
●空闲线模式在地址字节前预留一个静态空间,该模式没有额外的地址/数据位。它在处理包含lO个以上字节的数据块传输方面比地址位模式效率高。空闲线模式一般用于非 多处理器的SCI通信。
●地址位模式在每个字节中加入一个附加位(也就是地址位)。由于这种模式数据块之间不需要等待,因此在处理小块数据时比空闲线模式效率更高。
(4)控制SCI TX和RX的特性
用户可以使用软仵通过ADDR/IDLE MODE位(SCICCR,位3)选择多处理器模式,两种模式都使用TXWAKE(SCICTL1,位3)、RXWAKE(SCIRXST,位1)和SLEEP标志位(SCICTL1,位2)控制SCI的发送器和接收器的特性。
(5)接收步骤
在两种多处理器模式中,接收步骤如下:
①在接收地址块时,SCI端口唤醒并申请中断(必须使能SCICTL2的RX/BK INT ENA位申请中断),读取地址块的第一帧,该帧包含目的处理器的地址。
②通过中断检查接收的地址启动软件例程,然后比较内存中存放的器件地址和接收到数据的地址字节。
③如果上述地址相吻合表明地址块与DSP的地址相符,则CPU清除SLEEP位并读取块中剩余的数据;否则,退出软件子程序并保持SLEEP置位,直到下一个地址块的开始才接收中断。
经过认真的学习课程和观察库函数以及例程文件,终于知道baseAddress是什么了。
比如库函数中总是出现,例如
[size=13.6842107772827px]void [size=13.6842107772827px]EUSCI_A_UART_disable[size=13.6842107772827px] (uint16_t baseAddress)
这个很简单吧,我刚一看真不知道这个baseAddress是什么,后来看了后面的内容,原来只不过是个参变量,哎,脑子秀逗了,这个跟随便写个ABC,没什么区别。
只不过所有的基地址都起了这个名字,对应不同的外设时候才有不同的常量名字。比如在UART模块对应的基地址是EUSCI_A0_BASE
在IIC里面对应的基地址是EUSCI_B0_BASE
这下明白了。谢谢版主的教程。以后学习430看来更简单了。
15、
根据这个图,再看教程很清楚明了啊。
从上图可以清除的看出来,那个说的DCO的三个模式void CS_setupDCO( uint8_t mode )
使用参数所选模式配置 DCO。 如果选择旁路模式,则需要在 CLKIN 管脚接入外部数字时钟
信号来作为所有的设备(CPU、外设等) 的时钟信号。 ACLK 频率是不可以被编程的, 且固定在
旁路时钟频率除以 512。 使用外部吊足模式, 需要在 ROSC 管脚连接一个 20KΩ的电阻器。 与使
用内部电阻相比, 使用外部电阻模式,在绝对误差和温度漂移上, 可以提供更高的时钟精度。 请
根据你所选的设备型号对应的数据手册的详细情况来选择不同的模式。
该函数只有一个参数: mode。
mode: 该参数可以选择的量有 CS_INTERNAL_RESISTOR, CS_EXTERNAL_RESISTOR,
CS_BYPASS_MODE。
就是这个函数,图上给出了,对应的Rint,Rext,CLKIN,其中对DCOBYP置位时候选择CLKIN通道,就是传说中大家疑问的旁路模式,CS_BYPASS_MODE。
[size=16.470588684082px]也就是说旁路模式就是通过DCOBYP将输入旁路到外部的CLKIN,那么当然就电路复杂了,需要外部时钟源设备,还有使用Rext,就是选择外部电阻模式,就需要在ROSC上安装个电阻。选择内部电阻就是默认使用DCO输出16.384MHz.如果需要使用其他频率,需要通过编程CSIRFCAL,CSIRTCAL这两个寄存器。一般不推荐。
[size=16.470588684082px]另外如果使用外部电阻,则是使用20-kΩ 0.1% ±50-ppm/°C
这个规格的电阻,不过也是不推荐,毕竟内部有了电阻了,你再用外部的,不一定准确。同样,如果更改DCO发生频率,需要修改寄存器,不过库函数没有给出方法,那就是,不建议这样用。
uint32_t CS_getMCLK(void); uint32_t CS_getSMCLK(void); uint32_t CS_getACLK(void);这三个函数是获取当前的时钟频率。1. 文中写到此外,使用这个API前应该调用CS_setupDCO(),以便DCO被校准,这样计算才是精确的。 补充一下啊,获取这个API其实还要调用void CS_initClockSignal (uint8_t clockSource, uint8_t clockSourceDivider)函数配置MCLK,SMCLK分频系数,不然怎么获得时钟频率呢。 不过我们一般在MAIN函数开始就会先调用CS_setupDCO和CS_initClockSignal 这两个函数来初始化时钟了。 之后如果在调用uint32_t CS_getMCLK(void); uint32_t CS_getSMCLK(void); uint32_t CS_getACLK(void);这几个函数应该就不用在用CS_setupDCO这个函数了吧。
2.这三个函数都有说明: 当设备安装在DCO旁路模式下,它不能正常工作. 因为旁路模式使用的是外部晶振,MCU本身不知道晶振频率故当然也不能获取MCLK,ACLK频率了。
3.虽但说这第三个函数 当设备安装在DCO旁路模式下,它不能正常工作。 但是也有可能因为不知道误调用了这几个函数,编译器也不会报错啊。
但是头文件中看这个函数使用。
//*****************************************************************************//
//! \brief Get the current SMCLK frequency in Hz
//!
//! This API returns the current SMCLK frequency in Hz. It does not work when
//! the device is setup in DCO bypass mode. Also, CS_setupDCO() should be
//! called before this API so that the DCO has been calibrated and this
//! calculation is accurate.
//!
//!
//! \return Current SMCLK frequency in Hz, 0 when in bypass mode
//
//*****************************************************************************
extern uint32_t CS_getSMCLK(void);
注意红色部分,在bypass mode模式返回0. 那我们看源文件。
uint32_t CS_getSMCLK(void) {
uint8_t divider = (CSCTL1 & 0x70) >> 4;
return(CS_DCO_FREQ >> divider);
}
如果使用bypass模式作为DCO时钟,在调用设置SMCLK分频CS_initClockSignal ,如果调用CS_getSMCLK返回就是错误的频率。
则返回的要源文件函数中也不会返回0,这里写的感觉有点问题,不知道个人理解的对不对。
个人感觉可以这样写
uint32_t CS_getSMCLK(void) {
uint8_t divider = (CSCTL1 & 0x70) >> 4;
if(in DCO bypass mode)
return 0;
else
return(CS_DCO_FREQ >> divider);
} 这样就对应cs。h头文件的说明了,哈哈。。
针对DCO利用外部电阻产生时钟模式下,可以通过ROSC管脚连接一个外部高精度电阻,如原文所示:When DCOR bit is set to 1 by the application, the DCO selects the external resistor mode for operation. It is recommended to connect a 20-kΩ 0.1% ±50-ppm/°C resistor at the ROSC pin of the device for operating in the external resistor mode. This mode offers higher clock accuracy in terms of absolute tolerance and temperature drift compared to internal resistor mode.
但是文中并没有对于此高精度电阻的特性给以明确说明,是用金属膜电阻还是碳膜电阻。同时对于电阻的封装形式有没有一个规定。比如最小得多大的封装。等等。对于不同特性电阻(精度相同,温漂特性相同的条件下)连接下,所产生的时钟的区别有多大?
闲着没事,就把课后题给写了一下,根据提供的例程改写。用的是STC单片机串口接收思维,贴出来请各位大神指正,新手,多多包涵
#include <msp430.h>
volatile uint8_t T[7] = "LED_OFF";
volatile uint8_t R[7] = "LED_ON";
volatile uint8_t E[7] = "ERROR";
volatile uint8_t Rec[10];
volatile uint8_t flag=3;
int main(void) {
EUSCI_A_UART_initParam uartConfig = {
EUSCI_A_UART_CLOCKSOURCE_SMCLK, // SMCLK Clock Source
8, // BRDIV = 8
14, // UCxBRF = 14
34, // UCxBRS = 34
EUSCI_A_UART_NO_PARITY, // No Parity
EUSCI_A_UART_MSB_FIRST, // MSB First
EUSCI_A_UART_ONE_STOP_BIT, // One stop bit
EUSCI_A_UART_MODE, // UART mode
EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION // Oversampling Baudrate
};
WDT_hold(WDT_BASE);
// Setting the DCO to use the internal resistor. DCO will be at 16.384MHz
CS_setupDCO(CS_INTERNAL_RESISTOR);
// SMCLK should be same speed as DCO. SMCLK = 16.384MHz
CS_initClockSignal(CS_SMCLK, CS_CLOCK_DIVIDER_1);
// Settings P1.2 and P1.3 as UART pins. P1.4 as LED output
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
GPIO_PIN2 | GPIO_PIN3,
GPIO_PRIMARY_MODULE_FUNCTION);
GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN4);
GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4);
// Configure and enable the UART peripheral
EUSCI_A_UART_init(EUSCI_A0_BASE, &uartConfig);
EUSCI_A_UART_enable(EUSCI_A0_BASE);
EUSCI_A_UART_enableInterrupt(EUSCI_A0_BASE,
EUSCI_A_UART_RECEIVE_INTERRUPT);
while(1)
{
uint8_t i;
while(EUSCI_A_UART_receiveData(EUSCI_A0_BASE)!='') //读取接收到的信息,如果不为空,则开始接收
for(i=0;i<8;i++)
Rec[i]=EUSCI_A_UART_receiveData(EUSCI_A0_BASE); //将接收到的信息放到数组Rec中
if(Rec[5]=='N') //判断接收到数组的第五位。因为LED_ON、LED_OFF与LED_TEST从第五位开始不同,如果为N,则为LED_ON
{
GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN4);
GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4);
flag=0; //点亮LED的同时,置标志位flag=0;当flag=0时,说明是灯是开着的
}
else if(Rec[5]=='F') //判断接收到数组的第五位。因为LED_ON、LED_OFF与LED_TEST从第五位开始不同,如果为F,则为LED_OFF
{
GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN4);
GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN4);
flag=1; //关闭LED的同时,将flag=1;说明此时为LED_OFF
}
else if(Rec[5]=='E') //判断接收到数组的第五位。因为LED_ON、LED_OFF与LED_TEST从第五位开始不同,如果为E,则为LED_TEST
{
if(flag==0) //读取此时的标志位,若flag=0;则说明是LED_ON,然后将R【7】=LED_ON发送出去
{
for(i=0;i<8;i++)
EUSCI_A_UART_transmitData(EUSCI_A0_BASE, R[i]);
}
else if(flag==1) //读取此时的标志位,若flag=1;则说明是LED_OFF,然后将R【7】=LED_OFF发送出去
{
for(i=0;i<8;i++)
EUSCI_A_UART_transmitData(EUSCI_A0_BASE, T[i]);
}
else //读取此时的标志位,若flag既不是0也不是1,;则发送ERROR
{
for(i=0;i<8;i++)
EUSCI_A_UART_transmitData(EUSCI_A0_BASE, E[i]);
}
}
__bis_SR_register(LPM0_bits | GIE);
}
}
A提问:
我的板子上引出了P1.1~P1.4等多个外接IO,均上拉,用于外部中断输入。我只打开其中某个IO中断(比如P1.1),其他端口
禁止中断,但是外部向P1.1发生中断时,其他端口(P1.2~P1.3)中断标志位有时候居然也会被置位,非常奇怪!
B回复:
检查复位是否正常,可以添加点亮LED灯的程序判断复位是否正常和程序是否运行
A追问:
LPM3唤醒
for(;;)
{
// Stop WDT
WDTCTL = WDTPW + WDTHOLD;
// Enter LPM3 w/interrupt
_BIS_SR(LPM3_bits + GIE);
// Start WDT
WDTCTL = WDT_ARST_1000;
//SysLoopTask
do_SysLoopTask();
}//for
并且程序中使用了1秒定时器。在外接Jtag下载器时,程序运行一切正常,每隔1秒从睡眠中醒来,并且也能够响应外部IO中断;
但是,如果去掉下载器让板子单独运行,程序就睡眠而不被外部中断或者内部的定时器唤醒。
B回复:
for(;;)
{
// Start WDT_timer
WDTCTL = WDT_ARST_1000;
// Enter LPM3 w/interrupt
_BIS_SR(LPM3_bits + GIE);
//SysLoopTask
do_SysLoopTask();
}//for
IO复用看下!
建议多看看TI的手册和范例!430很简单!
MSP430有三个时钟源,三个时钟信号批发商,N个时钟用户。
1、时钟源
(1)DCO时钟,芯片内部时钟,系统启动时默认的工作时钟,但是不够稳定,容易随着电压和温度变化,不过,还好,芯片一般自带FLL锁相环装置,能够在一定程度上调整偏差。4系列处理器默认工作频率1.048576MHz,其他的处理器800kHz左右。
(2)LFXT1,外部低频时钟,一般接32768Hz的晶振,无需旁接电容(有内部电容,可以通过FLL_CTL0的XCAPxPF选择不同的电容值),相应的引脚为XTIN和XTOUT。如果非得让LFXT1工作在高频模式,那么需要将FLL_CTL0寄存器(地址0053)的XTS_FLL置位。
例如: FLL_CTL0 |= XCAP10PF + XTS_FLL ; //LFXT1内接10PF的电容 + LFXT1工作在高频
注:不同型号的芯片可以选择的电容值不同,具体查看芯片手册。
(3)XT2,外部高频时钟,该时钟需要外接谐振电容,引脚为XT2IN、XT2OUT。工作频率为450K-8MHz之间,可选的电容值一般为2pF左右。
注:无论是LFXT1还是XT2,晶振都要尽量靠近引脚,且用地线包围晶振信号线。
2、时钟信号批发商
时钟信号批发商把时钟源打包配置后,销售给各个时钟终端用户。
具体方式为,通过配置SCFQCTL、SCFI0、SCFI1、FLL_CTL0、FLL_CTL1将各个时钟进行分频,然后提供给mcu以及外围模块,并提供周到服务,保证时钟工作稳定,如果不稳定,免费提供更换服务,如:当检测到LFXT1失效时,自动切换到DCO,不过一般来说发生退货的可能性不大,所以不必太多考虑。
这三个批发商为ACKL、MCLK、SMCLK,分别称为辅助时钟、主时钟、sm时钟,别想歪了,SM是Sub Main 、Clock,不是………… :P 。
(1)ACLK只能做LFXT1的代理,它将LFXT1进行1/2/4/8分频之后,提供给外围模块。不用的时候还可以关闭,但是该时钟不可以被单独关闭。ACLK一般用于低速外设。
例如:MOV #OSCOFF, SR //关闭所有的晶振,一般关闭晶振osc的时候,必然同时关闭CPU,也就是低功耗6模式,但是关闭CPU,不一定关闭osc。
(2)MCLK的货源可以是LFXT1、XT2、DCO三者之一,包装后的频率可以是1/2/4/8/off。由于该时钟可以被关闭,所以可以用来降低功耗。
(3)SMCLK可以来自DCO和XT2,或者DCO和LFXT1,根据具体的芯片型号不同而有区别。SMCLK一般用于高速外设。
例如:MOV #SMCLKOFF, &FLL_CTL1 //关闭smclk时钟
MOV #XT2OFF + SELM0 + SELS + FLL_DIV_2, &FLL_CTL1
//关闭XT2晶振,MCLK选择DCO,SMCLK选择XT2,ACLK被2分频(来自LFXT1)
总结:系统每次启动后,默认使用DCO作为MCLK和SMCLK时钟来源,ACLK时钟来自LFXT1,这些配置可以在系统启动后在程序中修改。
1、CPU运行在VLO时钟下:
这是最慢的时钟,在约12千赫兹下运行。因此,我们将通过可视化的LED闪烁的红色慢慢地在约每3秒钟率。我们可以让时钟系统默认这种状态,设置专门来操作VLO。我们将不使用任何ALCK外设时钟在此实验室工作,但你应该认识到,ACLK来自VLO时钟。
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器
P1DIR = 0x40; // P1.6 配置输出
P1OUT = 0; // 关闭LED
BCSCTL3 |= LFXT1S_2; // LFXT1 = VLO
IFG1 &= ~OFIFG; // 清除OSCFault 标志
__bis_SR_register(SCG1 + SCG0); // 关闭 DCO
BCSCTL2 |= SELM_3 + DIVM_3; // MCLK = VLO/8
while(1)
{
P1OUT = 0x40; // 开启LED
_delay_cycles(100);
P1OUT = 0; // 关闭 LED
_delay_cycles(5000);
}
}
2、CPU运行在晶振(32768Hz)时钟下:
晶体频率为32768赫兹,约3倍的VLO。如果我们在前面的代码中使用晶振,指示灯应闪烁大约每秒一次。你知道为什么32768赫兹是一个标准?这是因为这个数字是2的15次方,因此很容易用简单的数字计数电路,以每秒一次获得率 ——手表和其他时间时基。认识到ACLK来自外部晶振时钟。
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器
P1DIR = 0x41; // P1.0 和P1.6配置输出
P1OUT = 0x01; // 开启P1.0
BCSCTL3 |= LFXT1S_0; // LFXT1 = 32768Hz 晶振
while(IFG1 & OFIFG)
{
IFG1 &= ~OFIFG; // 清除 OSCFault 标志
_delay_cycles(100000); // 为可见的标志延时
}
P1OUT = 0; // 关闭P1
__bis_SR_register(SCG1 + SCG0); // 关闭 DCO
BCSCTL2 |= SELM_3 + DIVM_3; // MCLK = 32768/8
while(1)
{
P1OUT = 0x40; // 开启 LED
_delay_cycles(100);
P1OUT = 0; / / 关闭LED
_delay_cycles(5000);
}
}
3、CPU运行在晶振(32768Hz)和DCO时钟下:
最慢的频率,我们可以运行DCO约在1MHz(这也是默认速度)。因此,我们将开始切换MCLK到DCO下。在大多数系统中,你会希望ACLK上运行的VLO或32768赫兹晶振。由于ACLK在我们目前的代码是在晶体上运行,我们会打开DCO计算。
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器
if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)
{
while(1); // If cal const erased, 挂起
}
BCSCTL1 = CALBC1_1MHZ; // Set range
DCOCTL = CALDCO_1MHZ; //设置DCO模式
P1DIR = 0x41; // P1.0 和P1.6配置输出
P1OUT = 0x01; // P1.0 开启
BCSCTL3 |= LFXT1S_0; // LFXT1 = 32768Hz
while(IFG1 & OFIFG)
{
IFG1 &= ~OFIFG; // 清除OSCFault 标志
_delay_cycles(100000); // 为可见标志延时
}
P1OUT = 0; // P1.6 关闭
// __bis_SR_register(SCG1 + SCG0); // 关闭DCO
BCSCTL2 |= SELM_0 + DIVM_3; // MCLK = DCO
while(1)
{
P1OUT = 0x40; // P1.6 开启
_delay_cycles(100);
P1OUT = 0; / / P1.6 关闭
_delay_cycles(5000);
}
}
4、CPU运行在DCO时钟下:
最慢的频率,我们可以运行DCO约在1MHz(这也是默认速度)。因此,我们将开始切换MCLK到DCO下。在大多数系统中,你会希望在VLO或者是晶振下运行ACLK。由于ACLK在我们目前的代码是在VLO上运行,我们会打开DCO运行。
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器
if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)
{
while(1); // If cal const erased,挂起
}
BCSCTL1 = CALBC1_1MHZ; // Set range
DCOCTL = CALDCO_1MHZ; // 设置DCO模式
P1DIR = 0x40; // P1.6 配置输出
P1OUT = 0; // P1关闭
BCSCTL3 |= LFXT1S_2; // LFXT1 = VLO
IFG1 &= ~OFIFG; // 清除 OSCFault 标志
//__bis_SR_register(SCG1 + SCG0); // 关闭DCO
BCSCTL2 |= SELM_0 + DIVM_3; // MCLK = DCO/8
while(1)
{
P1OUT = 0x40; // P1.6 关闭
_delay_cycles(100);
P1OUT = 0; // P1.6 开启
_delay_cycles(5000);
}
}
1、CPU运行在VLO时钟下:
这是最慢的时钟,在约12千赫兹下运行。因此,我们将通过可视化的LED闪烁的红色慢慢地在约每3秒钟率。我们可以让时钟系统默认这种状态,设置专门来操作VLO。我们将不使用任何ALCK外设时钟在此实验室工作,但你应该认识到,ACLK来自VLO时钟。
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器
P1DIR = 0x40; // P1.6 配置输出
P1OUT = 0; // 关闭LED
BCSCTL3 |= LFXT1S_2; // LFXT1 = VLO
IFG1 &= ~OFIFG; // 清除OSCFault 标志
__bis_SR_register(SCG1 + SCG0); // 关闭 DCO
BCSCTL2 |= SELM_3 + DIVM_3; // MCLK = VLO/8
while(1)
{
P1OUT = 0x40; // 开启LED
_delay_cycles(100);
P1OUT = 0; // 关闭 LED
_delay_cycles(5000);
}
}
2、CPU运行在晶振(32768Hz)时钟下:
晶体频率为32768赫兹,约3倍的VLO。如果我们在前面的代码中使用晶振,指示灯应闪烁大约每秒一次。你知道为什么32768赫兹是一个标准?这是因为这个数字是2的15次方,因此很容易用简单的数字计数电路,以每秒一次获得率 ——手表和其他时间时基。认识到ACLK来自外部晶振时钟。
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器
P1DIR = 0x41; // P1.0 和P1.6配置输出
P1OUT = 0x01; // 开启P1.0
BCSCTL3 |= LFXT1S_0; // LFXT1 = 32768Hz 晶振
while(IFG1 & OFIFG)
{
IFG1 &= ~OFIFG; // 清除 OSCFault 标志
_delay_cycles(100000); // 为可见的标志延时
}
P1OUT = 0; // 关闭P1
__bis_SR_register(SCG1 + SCG0); // 关闭 DCO
BCSCTL2 |= SELM_3 + DIVM_3; // MCLK = 32768/8
while(1)
{
P1OUT = 0x40; // 开启 LED
_delay_cycles(100);
P1OUT = 0; / / 关闭LED
_delay_cycles(5000);
}
}
3、CPU运行在晶振(32768Hz)和DCO时钟下:
最慢的频率,我们可以运行DCO约在1MHz(这也是默认速度)。因此,我们将开始切换MCLK到DCO下。在大多数系统中,你会希望ACLK上运行的VLO或32768赫兹晶振。由于ACLK在我们目前的代码是在晶体上运行,我们会打开DCO计算。
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器
if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)
{
while(1); // If cal const erased, 挂起
}
BCSCTL1 = CALBC1_1MHZ; // Set range
DCOCTL = CALDCO_1MHZ; //设置DCO模式
P1DIR = 0x41; // P1.0 和P1.6配置输出
P1OUT = 0x01; // P1.0 开启
BCSCTL3 |= LFXT1S_0; // LFXT1 = 32768Hz
while(IFG1 & OFIFG)
{
IFG1 &= ~OFIFG; // 清除OSCFault 标志
_delay_cycles(100000); // 为可见标志延时
}
P1OUT = 0; // P1.6 关闭
// __bis_SR_register(SCG1 + SCG0); // 关闭DCO
BCSCTL2 |= SELM_0 + DIVM_3; // MCLK = DCO
while(1)
{
P1OUT = 0x40; // P1.6 开启
_delay_cycles(100);
P1OUT = 0; / / P1.6 关闭
_delay_cycles(5000);
}
}
4、CPU运行在DCO时钟下:
最慢的频率,我们可以运行DCO约在1MHz(这也是默认速度)。因此,我们将开始切换MCLK到DCO下。在大多数系统中,你会希望在VLO或者是晶振下运行ACLK。由于ACLK在我们目前的代码是在VLO上运行,我们会打开DCO运行。
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器
if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)
{
while(1); // If cal const erased,挂起
}
BCSCTL1 = CALBC1_1MHZ; // Set range
DCOCTL = CALDCO_1MHZ; // 设置DCO模式
P1DIR = 0x40; // P1.6 配置输出
P1OUT = 0; // P1关闭
BCSCTL3 |= LFXT1S_2; // LFXT1 = VLO
IFG1 &= ~OFIFG; // 清除 OSCFault 标志
//__bis_SR_register(SCG1 + SCG0); // 关闭DCO
BCSCTL2 |= SELM_0 + DIVM_3; // MCLK = DCO/8
while(1)
{
P1OUT = 0x40; // P1.6 关闭
_delay_cycles(100);
P1OUT = 0; // P1.6 开启
_delay_cycles(5000);
}
}
MSP430的时钟周期(振荡周期)、机器周期、指令周期之间的关系
时钟周期也称为振荡周期:定义为时钟脉冲的倒数(时钟周期就是直接供内部CPU使用的晶振的倒数,例如12M的晶振,它的时钟周期就是1/12us),是计算机中的最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。时钟脉冲是计算机的基本工作脉冲,控制着计算机的工作节奏。时钟频率越高,工作速度就越快。
机器周期:在计算机中,常把一条指令的执行过程划分为若干个阶段,每一个阶段完成一项工作。每一项工作称为一个基本操作,完成一个基本操作所需要的时间称为机器周期。8051系列单片机的一个机器周期由6个S周期(状态周期)组成。一个S周期=2个时钟周期,所以8051单片机的一个机器周期=6个状态周期=12个时钟周期。
指令周期:执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期也不同。
专用知识:
在430中,一个时钟周期= MCLK晶振的倒数。如果MCLK是8M,则一个时钟周期为1/8us;
一个机器周期 =一个时钟周期,即430每个动作都能完成一个基本操作;
一个指令周期 = 1~6个机器周期,具体根据具体指令而定。
另:指令长度,只是一个存储单位与时间没有必然关系。
MSP430根据型号的不同最多可以选择使用3个振荡器。我们可以根据需要选择合适的振荡频率,并可以在不需要时随时关闭振荡器,以节省功耗。这3个振荡器分别为:
(1)DCO 数控RC振荡器。它在芯片内部,不用时可以关闭。DCO的振荡频率会受周围环境温度和MSP430工作电压的影响,且同一型号的芯片所产生的频率也不相同。但DCO的调节功能可以改善它的性能,他的调节分为以下3步:a:选择BCSCTL1.RSELx确定时钟的标称频率;b:选择DCOCTL.DCOx在标称频率基础上分段粗调;c:选择DCOCTL.MODx的值进行细调。
(2)LFXT1 接低频振荡器。典型为接32768HZ的时钟振荡器,此时振荡器不需要接负载电容。也可以接450KHZ~8MHZ的标准晶体振荡器,此时需要接负载电容。
(3)XT2 接450KHZ~8MHZ的标准晶体振荡器。此时需要接负载电容,不用时可以关闭。
低频振荡器主要用来降低能量消耗,如使用电池供电的系统,高频振荡器用来对事件做出快速反应或者供CPU进行大量运算。当然高端430还有锁频环(FLL)及FLL+等模块,但是初步不用考虑那么多。
MSP430的3种时钟信号:MCLK系统主时钟;SMCLK系统子时钟;ACLK辅助时钟。
(1)MCLK系统主时钟。除了CPU运算使用此时钟以外,外围模块也可以使用。MCLK可以选择任何一个振荡器所产生的时钟信号并进行1、2、4、8分频作为其信号源。
(2)SMCLK系统子时钟。供外围模块使用。并在使用前可以通过各模块的寄存器实现分频。SMCLK可以选择任何一个振荡器所产生的时钟信号并进行1、2、4、8分频作为其信号源。
(3)ACLK辅助时钟。供外围模块使用。并在使用前可以通过各模块的寄存器实现分频。但ACLK只能由LFXT1进行1、2、4、8分频作为信号源。
PUC复位后,MCLK和SMCLK的信号源为DCO,DCO的振荡频率默认为800KHZ。ACLK的信号源为LFXT1。
MSP430内部含有晶体振荡器失效监测电路,监测LFXT1(工作在高频模式)和XT2输出的时钟信号。当时钟信号丢失50us时,监测电路捕捉到振荡器失效。如果MCLK信号来自LFXT1或者XT2,那么MSP430自动把MCLK的信号切换为DCO,这样可以保证程序继续运行。但MSP430不对工作在低频模式的LFXT1进行监测。
为了实现具体的时钟可以设置跟时钟相关的寄存器,在低端430中是DCOCTL、BCSCTL1和BCSCTL2三个寄存器。而对于高端的430,则要考虑SCFI0、SCFQCTL、FLL_CTL0、FLL_CTL1和BTCTL等几个寄存器。具体设置,参看DataSheet。
单片机上电后,如果不对时钟系统进行设置,默认800 kHz的DCOCLK为MCLK和SMCLK的时钟源,LFXTl接32768 Hz晶体,工作在低频模式(XTS=O)作为ACLK的时钟源。CPU的指令周期由MCLK决定,所以默认的指令周期就是1/800 kHz="1".25μs。要得到lμs的指令周期需要调整DCO频率,即MCLK=1 MHz,只需进行如下设置:BCSCTLl=XT20FF+RSEL2;
//关闭XT2振荡器,设定DCO频率为1 MHz
我认真学习了教程后,对CS系统有一个很好的认识。
第一,关于时钟信号的来源,系统内部提供了DCO压控振荡器是第一个来源,第二个来源是CLKIN管脚的,使用第二个来源需要使用DCO的旁路模式,就是把DCO搞到一边去,旁路到一边去,没它什么事。
第二,所谓的MCLK,ACLK,SMCLK,以及供给SD24的LK,这四个都是从时钟信号源接收信号后经过分频处理获得不同频率,用于不同目的的信号分频与分配系统。
这些不能称为时钟源,只能叫做时钟信号源信号处理与分配器,这样更恰当,不好理解的请看时钟系统模块图。主贴已经给了那个手册的下载附件。
以上两点就是我对教程中CS的认识,谢谢。我是认真思考后才来参与回复的
关于第4章节的串口通信部分,我说一下我的看法,关于通信肯定需要时钟信号进行同步,本系列单片机的时钟信号来源有3种,SMCLK,ACLK,UCLK。
UCLK这个不仔细读教程和手册真不知道干啥的,手册说是,external eUSCI clock,也就是外部的eUSCI时钟。
查看封装图,可知道P1.1/UCA0CLK/SMCLK/TMS P1.0/UCA0STE/MCLK/TCK
另外P1.3和P1.2分别是UART的TXD和RXD,也就是说,1.1和1.0具备输出SMCLK和输出MCLK的功能。
UCA0CLK:eUSCI_A0 clock input/output (direction controlled by eUSCI)。这一点就解释了部分朋友对如何获取这些时钟信号输出的问题了,这需要参考对应具体型号的数据手册,不同型号内部资源是不同的。
首先本课程是介绍的MSP430i2xx系列,虽然都是2系列,跟以往F2xx,以及超值G2xx系列还是有很大的不同的,重要的一点时钟系统,就很不同,这个i2系列为了保证系统的安全可靠的工业级,去掉了容易因为外部原因造成的时钟系统失败,比如晶振的问题。等等。我看了看,楼主的教程根本没有提到晶振,也就是该单片机根本不需要晶振,因为i2xx把晶振的震荡系统去掉了。毕竟好多单片机启动失败一大部分原因是晶振坏了,比如我买的USB声卡没用多久就坏了,检测就是晶振坏了,还有U盘,如果连接电脑无法识别也是晶振坏了。所以i2xx为了保证工业品质,去掉了容易出错了地方,特别采用了强化的DCO,该DCO跟以往的G2xx的DCO也是不同的,该DCO具备两种电阻匹配模式,一个内置的,一个外置的,从前的好像没有外置的,所以也就没有区分,我看了G2的手册做了对比,这个真没有,所以该i2xx系列独到之处就在于此,以前的DCO只能采用内部的电阻配合DCO起振,由于电阻具有热变效应,也就是温度不同阻抗会发生变化,所以MCU工作肯定发热,会影响到内部的电阻阻值,所以提供的外置接口ROSC,即芯片的第11个PIN,而且是非共用的PIN,这说明系统设计师对该端口多么的看重。
根据要求写了课后作业,分别是串口发送和接收代码。
#define LED_OFF 0x00
#define LED_ON 0x01
#define LED_TEST 0x02
uint8_t LED_Status = 0x00;
uint8_t flag = 0x00;
Void UARTTRANSMIT(void)
{
//发送LED_ON指令,点亮LED
EUSCI_A_UART_ transmitData(EUSCI_A0_BASE, LED_ON);
// 发送空指令,等待串口接收指令并操作
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
//发送LED_ON指令,点亮LED
EUSCI_A_UART_ transmitData(EUSCI_A0_BASE, LED_OFF);
// 发送空指令,等待串口接收指令并操作
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
//发送LED_ON指令,点亮LED
EUSCI_A_UART_ transmitData(EUSCI_A0_BASE, LED_TEST);
// 发送空指令,等待串口接收指令并操作
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
Void UARTRECEIVE(void)
{
Uint8_t ReceData = 0x00;
//接收指令
ReceData = uint8_t EUSCI_A_UART_receiveData ( EUSCI_A0_BASE);
//判断接收值
Switch (ReceData)
{
//接收值为0x00,关掉LED,置标志位为0
Case 0x00:
{
GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4);
flag = 0;
}break;
//接收值为0x01,开启LED,置标志位为1
Case 0x01:
{
GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN4);
flag = 1;
}break;
//接收值为0x02,根据标志位来设置LED_Status的值
Case 0x02:
{
If(flag==0)
{
LED_Status = 0x00;
}
Else
{
LED_Status = 0x01;
}
}break;
Default:break;
}
}
设置不同的SMCLK频率配置UART模块波特率为115200
会有多种不同的方式
CS_initClockSignal(CS_SMCLK, CS_CLOCK DIVIDER_1);
CS_initClockSignal(CS_SMCLK, CS_CLOCK DIVIDER_2);
CS_initClockSignal(CS_SMCLK, CS_CLOCK DIVIDER_4);
CS_initClockSignal(CS_SMCLK, CS_CLOCK DIVIDER_8);
CS_initClockSignal(CS_SMCLK, CS_CLOCK DIVIDER_16);
因而对应的不同的初始化配置
EUSCI_A_UART_ initParam uartConfig = {
EUSCI_A_UART_ CLOCKSOURCE SMCLK, // SMCLK Clock Source
BRDIV
UCxBRF,
UCxBRS ,
EUSCI_A_UART_ NO PARITY, // No Parity
EUSCI_A_UART_ MSB FIRST, // MSB First
EUSCI_A_UART_ ONE STOP BIT, // One stop bit
EUSCI_A_UART_ MODE, // UART mode
EUSCI_A_UART_ OVERSAMPLING_BAUDRATE_GENERATION // Oversampling Baudrate
};
如下图所示的计算可方面设置
BRDIV,UCxBRF,UCxBRS ,
仔细考虑下,配置不同的外设时钟,时钟越低,功耗应该就相对越低了,所以从功耗的角度考虑选择低频应该好些
但是由于需要配置115200 的高串行通信速率,时钟太低,配置后的波特率是否有误差还是一个问题,如果误差过大就会有误码的可能、
所以应该还是需要从多个角度考虑下
A提问:
看到例程上有这么一段程序:
CS_initClockSignal(CS_MCLK, CS_CLKOCK_DIVIDER_8);
CS_initClockSignal(CS_SMCLK, CS_CLKOCK_DIVIDER_2);
我想问下,这个MCLK和SMCLK一个8分频一个是2分频,他们在选择分频数值的时候是根据什么选择的?还有就是想问一下:8分频之后是不是比2分频之后的频率值小了?为什么MCLK的频率值要比SMCLK的频率值小呢?个人感觉应该要大一些才对…
B回复:
void CS_setupDCO( uint8_t mode )
使用参数所选模式配置DCO。如果选择旁路模式,则需要在CLKIN管脚接入外部数字时钟信号来作为所有的设备(CPU、外设等)的时钟信号。ACLK频率是不可以被编程的,且固定在旁路时钟频率除以512。使用外部吊足模式,需要在ROSC管脚连接一个20KΩ的电阻器。与使用内部电阻相比,使用外部电阻模式,在绝对误差和温度漂移上,可以提供更高的时钟精度。请根据你所选的设备型号对应的数据手册的详细情况来选择不同的模式。
该函数只有一个参数:mode。
mode:该参数可以选择的量有CS_INTERNAL_RESISTOR,CS_EXTERNAL_RESISTOR,CS_BYPASS_MODE。
返回值:空。
注释:DCO的配置可以使用内部电阻和外部电阻,还有一个就是旁路模式,旁路模式就是可以通过一个时钟输入管脚将外部时钟信号灌入系统。
首先是要选择时钟源,比如 CS_setupDCO(CS_INTERNAL_RESISTOR); 也就是配置时钟的内部电阻模式
DCO频率为16.384MHz.
CS_initClockSignal(CS_MCLK, CS_CLKOCK_DIVIDER_8);
表示将主系统的时钟配置为2.048MHZ
CS_initClockSignal(CS_SMCLK, CS_CLKOCK_DIVIDER_2);
表示将子系统的时钟配置为8.196MHZ
所以肯定是子系统的时钟频率高了
A提问:
看到例程上有这么一段程序:
CS_initClockSignal(CS_MCLK, CS_CLKOCK_DIVIDER_8);
CS_initClockSignal(CS_SMCLK, CS_CLKOCK_DIVIDER_2);
我想问下,这个MCLK和SMCLK一个8分频一个是2分频,他们在选择分频数值的时候是根据什么选择的?还有就是想问一下:8分频之后是不是比2分频之后的频率值小了?为什么MCLK的频率值要比SMCLK的频率值小呢?个人感觉应该要大一些才对…
B回复:
void CS_setupDCO( uint8_t mode )
使用参数所选模式配置DCO。如果选择旁路模式,则需要在CLKIN管脚接入外部数字时钟信号来作为所有的设备(CPU、外设等)的时钟信号。ACLK频率是不可以被编程的,且固定在旁路时钟频率除以512。使用外部吊足模式,需要在ROSC管脚连接一个20KΩ的电阻器。与使用内部电阻相比,使用外部电阻模式,在绝对误差和温度漂移上,可以提供更高的时钟精度。请根据你所选的设备型号对应的数据手册的详细情况来选择不同的模式。
该函数只有一个参数:mode。
mode:该参数可以选择的量有CS_INTERNAL_RESISTOR,CS_EXTERNAL_RESISTOR,CS_BYPASS_MODE。
返回值:空。
注释:DCO的配置可以使用内部电阻和外部电阻,还有一个就是旁路模式,旁路模式就是可以通过一个时钟输入管脚将外部时钟信号灌入系统。
首先是要选择时钟源,比如 CS_setupDCO(CS_INTERNAL_RESISTOR); 也就是配置时钟的内部电阻模式
DCO频率为16.384MHz.
CS_initClockSignal(CS_MCLK, CS_CLKOCK_DIVIDER_8);
表示将主系统的时钟配置为2.048MHZ
CS_initClockSignal(CS_SMCLK, CS_CLKOCK_DIVIDER_2);
表示将子系统的时钟配置为8.196MHZ
所以肯定是子系统的时钟频率高了
发送LED_ON字符串点亮接收MCU的LED,发送LED_OFF,关闭接收MCU的LED,发送LED_TEST字符串,返回当前的LED状态。
//引用的库
#include "driverlib.h"
//需要传输的字符和LED状态变量
volatile uint8_t TXDataOne[8] = "LED_OFF";
volatile uint8_t TXDataTwo[8] = "LED_ON";
volatile uint8_t TXDataThree[8] = "LED_TEST";
volatile uint8_t RXData[8];
volatile uint8_t LEDState=0;
//配置UART和设置LED相对应的IO口
void Configuration()
{
//波特率为115200 使用的是SMCLK时钟为16.384MHz
EUSCI_A_UART_initParam uartConfig = {
EUSCI_A_UART_CLOCKSOURCE_SMCLK, // SMCLK Clock Source
8, // BRDIV = 8
14, // UCxBRF = 14
34, // UCxBRS = 34
EUSCI_A_UART_NO_PARITY, // No Parity
EUSCI_A_UART_MSB_FIRST, // MSB First
EUSCI_A_UART_ONE_STOP_BIT, // One stop bit
EUSCI_A_UART_MODE, // UART mode
EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION // Oversampling Baudrate
};
// Setting the DCO to use the internal resistor. DCO will be at 16.384MHz
CS_setupDCO(CS_INTERNAL_RESISTOR);
// SMCLK should be same speed as DCO. SMCLK = 16.384MHz
CS_initClockSignal(CS_SMCLK, CS_CLOCK_DIVIDER_1);
// Settings P1.2 and P1.3 as UART pins. P1.4 as LED output
//设置P1.2和P1.3作为UART引脚,P1.4位LED管脚
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
GPIO_PIN2 | GPIO_PIN3,
GPIO_PRIMARY_MODULE_FUNCTION);
GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN4);
GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4);
//使能中断
EUSCI_A_UART_init(EUSCI_A0_BASE, &uartConfig);
EUSCI_A_UART_enable(EUSCI_A0_BASE);
EUSCI_A_UART_enableInterrupt(EUSCI_A0_BASE,
EUSCI_A_UART_RECEIVE_INTERRUPT);
}
int main(void) {
WDT_hold(WDT_BASE);
uint8_t i=0;
Configuration();
//发送字符
while(1)
{
for(i=0;i<9;i++)
EUSCI_A_UART_transmitData(EUSCI_A0_BASE, TXDataOne[i]);
__delay_cycles(500000);
for(i=0;i<9;i++)
EUSCI_A_UART_transmitData(EUSCI_A0_BASE, TXDataTwo[i]);
__delay_cycles(500000);
for(i=0;i<9;i++)
EUSCI_A_UART_transmitData(EUSCI_A0_BASE, TXDataThree[i]);
__delay_cycles(500000);
}
}
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_A0_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(USCI_A0_VECTOR)))
#endif
void USCI_A0_ISR(void) {
uint8_t i=0;
switch(__even_in_range(UCA0IV, USCI_UART_UCTXCPTIFG))
{
case USCI_NONE: break;
case USCI_UART_UCRXIFG:
for(i=0;i<9;i++)
RXData[i] = EUSCI_A_UART_receiveData(EUSCI_A0_BASE);
//检查字符,如果接收到的,是LED_ON字符串点亮接收MCU的LED,是LED_OFF,关闭接收MCU的LED,是LED_TEST字符串返回当前的LED状态
if(RXData == "LED_ON")
{
GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN4);
LEDState=1;
}
else if(RXData == "LED_OFF")
{
GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN4);
LEDState=0;
}
else if(RXData == "LED_TEST")
{
EUSCI_A_UART_transmitData(EUSCI_A0_BASE, LEDState);
}
else break;
case USCI_UART_UCTXIFG: break;
case USCI_UART_UCSTTIFG: break;
case USCI_UART_UCTXCPTIFG: break;
default: break;
}
}