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.

[参考译文] CCS/MSP430g2553:以菊花链形式连接多个 SPI 器件

Guru**** 2562120 points
Other Parts Discussed in Thread: MSP430G2553

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/601889/ccs-msp430g2553-daisy-chaining-several-spi-devices

器件型号:MSP430G2553

工具/软件:Code Composer Studio

您好!

我想使用 MSP430 (主器件)控制四个菊花链 SPI 从器件。 我在执行此操作时遇到了几个问题:

我希望 SPI 时钟速率为16MHz、这是我的微控制器的最大时钟频率。 下面是我要做的:

BCSCTL1 = CALBC1_16MHz;//将范围设置为16MHz
DCOCTL = CALDCO_16MHz;//将 DCO 阶跃和调制设置为16MHz

UCA0BR0 |= 0x01;//波特率
UCA0BR1=0;

当我将时钟速率设置为16MHz、并观察 MSP430生成的 Din 和 SCLK 信号时、它看起来几乎像一个正弦波(上升时间真的很慢)。 当我将时钟减慢至1MHz 时、两个信号看起来不错。 我想以16MHz 的频率运行 SPI clk。 如何在不影响信号完整性的情况下做到这一点?

每个从器件都有8个开关、因此对于四个器件、我需要向从器件一发送32位。 然后、这些位应通过移位寄存器到达最后一个器件、我不知道如何执行此操作。 我将按如下方式发送8个位:

P1OUT &=~0x02; //选择器件
IF (IFG2 & UCA0TXIFG)//如果 USCI_A0 TX 缓冲器就绪
{
UCA0TXBUF = 0x2D;//通过 SPI 将数据发送到从

机}

IF (IFG2 & UCA0RXIFG);//如果 USCI_A0 RX 接收
到{
Received_ch = UCA0RXBUF;//存储接收到的数据
P1OUT |= 0x02;//取消选择器件,更新开关
}

有人能告诉我如何做到这一点吗?  

以下是供您参考的完整代码:  

#include "msp430G2553.h"


volatile char received_ch = 0;
int main (void){

WDTCTL =(WDTPW | WDTHOLD);//停止看门狗定时器

BCSCTL1 = CALBC1_16MHz;//将范围设置为16MHz
DCOCTL = CALDCO_16MHz;//将 DCO 阶跃和调制设置为16MHz

P1OUT &=~0x02;//芯片选择(P1.1)
P1DIR |= 0x02;//将引脚设置为输出方向
P1SEL = 0x14;//将特殊功能设置为定时器模块(P1.4->SCLK、P1.2->DIN)
P1SEL2 = 0x14;//通用串行通信
UCA0CTL1 = UCSWRST;//将模块置于复位模式以设置操作
UCA0CTL0 |= UCCKPH + UCMSB + UCSTM+ UCS0CTL1;// UCS0CTL0| UCA0CMP0 | UCCKP0 | UCA0| UCCKP0 | UCA0CMP0 | UCC1 | UCS0CMP= UCA0CMP0 | UCC1 | UCS0CMP0 | UCS0CL

//使用

SPI 模式
UCA0CTL1 &=~UCSWRST;//初始化 USCI 状态机

IE2 |= UCA0TXIE;//启用 USCI0 TX 中断
_BIS_SR (GIE)时,应清除波特率 UCA0BR1 = 0;//无调制;//启用 USCI0 TX 中断_BIS_SR (GIE);//启用全局中断

while (1);//无限循环
}

#pragma vector=USCIAB0TX_vector
__interrupt void USCI0TX_ISR (void)
{
P1OUT &=~0x02;//同步不为低电平有效。 当低电平时、当高电平数据被更新时、DIN 和移位寄存器被启用。

if (IFG2 & UCA0TXIFG)// if USCI_A0 TX buffer READY
{
UCA0TXBUF = 0x2D;//通过 SPI 发送数据到从
机}

if (((IFG2 & UCA0RXIFG));// if USCI_A0 RX received
{
received_ch = UCA0RXBUF;//未接收

到数据|= 0x02

};//选择器件|更新= 0x02}


  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我无法对您的器件进行可视化。 如果是4个级联移位寄存器、那么它只有一个具有32位"命令"缓冲器的(SPI)器件、您必须同时向所有(子)器件发送命令。 如果是4个 SPI 器件、则有4个芯片选择、在任何选择中、其他3个器件应保持脱离总线。 您能否提供器件型号?

    具体而言:
    1)"if ((IFG2 & UCA0RXIFG));"删除该分号。 它可能会导致事务以字节中的字节结尾。
    2) 2)完全不使用中断。 对于接近 CPU 速度的 SPI 速度、您可能会在 SPI 空闲时花费80%的时间。 它还会使您的生活变得不必要的复杂化。 只需编写一个小的 SPI_Tx()函数并将其调用4次。
    3) 3)我已经从 G2553以全速运行3个 SPI 器件(EEPROM)、并且时钟看起来正常。 缓慢上升/下降时间表明总线争用(可能是太强上拉?)。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    请参阅 查询的第二部分:从 Maxim 的应用手册3947 (或类似文档)开始  ,另请参阅 ,特别是 SLAU144J 的图16-2。

    并非所有 SPI 器件、例如端口扩展器、甚至"旧式" SISO 移位寄存器都可以轻松地以菊花链形式连接;请仔细选择您的器件。

    请仔细遵守 有关控制链器件 CS 的规则。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我使用的器件是来自模拟器件的 ADG714。 我使用的是数据表图7中显示的实现方案: www.analogue.com/.../ADG714_715.pdf

    感谢您对 if 语句的关注。 问题仍然存在。

    关于不使用中断循环:问题是我必须使用中断循环、因为我还有其他需要由 ISR 完成的任务、SPI 命令的时序取决于这些任务。 例如、我将从 ADC 中读取数据、当检测到某种情况时、需要关闭 ADG714的一个或多个开关。

    关于上拉电阻器:我实际上没有将微控制器连接到 SPI 器件。 我计划使用10k 电阻器。 但现在、我只看一下生成 SCLK、CS 和 MOSI 信号的引脚上的微控制器输出。 您是否认为上升时间延迟是因为实际未连接任何器件?

    谢谢、
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    你好,Sahar!

    [引用用户="Sahar Elyahoodaya"]您是否认为上升时间延迟是因为实际没有连接设备?

    不、肯定不会-边沿会随着额外的负载电容而减小、反之亦然。

    您使用的是 G2 LaunchPad 还是自制电路板? 在 LaunchPad 上,P1.2也通过跳线连接到 UART<->USB 转换器。 您应该断开连接。

    您有错误的 SPI 模式-数据是在下降沿捕获的、如果时钟空闲、则为第二个下降沿。 此外、您希望接收一些内容并将其存储在变量中、但未启用 SOMI 函数。

    无论如何-出于您的目的、我会执行如下操作(请注意、在再次将 SYNC 设置为高电平之前、我使用 RX 中断正确检测最后写入的字节-这会使传输速度减慢一点):

    #include "msp430g2553.h"
    #include "stdint.h"
    
    void adg714_start_transmission (void);
    
    volatile uint8_t adg714_Bytes[4]={0x15、0x25、0x35、0x45};// ADG714器件的随机字节
    volatile uint8_t bytecounter;
    
    
    {void
    WDTTL (void)= void WDTTL (void);(void) //停止看门狗计时器
    
    BCSCTL1 = CALBC1_16MHz; //将范围设置为16MHz
    DCOCTL = CALDCO_16MHz; //将 DCO 步长和调制设置为16MHz
    
    P1OUT = 0x02; // P1.1高电平上的 SYNC 将 ADG714 SCLK 和 DIN 缓冲器
    P1DIR 断电 = 0x02; //将 P1.1设置为输出方向
    
    P1SEL = 0x14; //为 UCA0SIMO (P1.2)和 UCA0CLK (P1.4) P1SEL2
    = 0x14设置特殊功能寄存器1; //为 UCA0SIMO (P1.2)和 UCA0CLK (P1.4)设置特殊功能寄存器2
    
    UCA0CTL1 = UCSWRST; //将 USCI 模块设置为复位状态
    UCA0CTL0 =(UCMSB | UCMST | UCSYNC); //时钟空闲、数据在第一个边沿发生变化、在第二个边沿捕获、MSB 在第一个边沿、主器件、同步模式
    UCA0CTL1 |= UCSSEL_2; // SMCLK 作为时钟源
    UCA0BR0=1; //预分频器1
    UCA0BR1 = 0;
    UCA0CTL1 &=~Ω UCSWRST; //从复位中释放 USCI 模块
    
    __bis_SR_register( GIE ); //启用全局中断
    
    while (1) //无限循环
    {
    //...
    
    if (发生什么事) //发送数据的事件触发器
    {
    adg714_start_transmission (); //启动传输
    }
    
    //...
    }
    }
    
    void adg714_start_transmission (void)
    {
    P1OUT &=~0x02; // SYNC 低电平为 ADG714 SCLK 和 DIN 缓冲器
    IFG2加电 &=~UCA0RXIFG; //清除暂挂的 RX 中断标志
    IE2 |= UCA0RXIE; //使
    能 USCIA0 RX 中断 bytecounter = 0; //重置字节发声器
    UCA0TXBUF = adg714_Bytes[bytecounter];//发送第一个字节
    }
    
    #pragma vector = USCIAB0RX_vector
    __interrupt void USCIAB0RX_ISR (void)
    {
    if (++bytecounter < 4) //尚未发送所有字节
    {
    IFG2 &=~UCA0RXIFG; //清除 RX 中断标志
    UCA0TXBUF = adg714_Bytes[bytecounter];//发送下一个字节
    }
    否则 //发送的所有字节
    为{
    IE2 &=~UCA0RXIE; //禁用 USCIA0 RX 中断
    P1OUT |= 0x02; //更新 ADG714输出
    }
    

    [代码未测试]

    Dennis

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Dennis:


    感谢您回答我的问题和示例代码。


    是的、我使用的是 G2 launchpad。 按照您的建议、我断开了将 UART 连接到引脚1.2的跳线5、然后运行了您提供的示例代码。 它在1MHz 下工作良好、但在更高的时钟频率下、它将像以前一样开始失去信号完整性。 我的意思是 SCLK 和 DIN 信号不再是急剧的3V 或 GRD。 相反、在任何高于1MHz 的频率下、它们将经历上升/下降时间延迟并包含振荡。 频率越高、问题就越严重。 您知道原因吗?


    如果我以1MHz 运行时钟、32位将需要超过160us 才能传输到4个从器件。 出于我的目的、这是一个很长的延迟。


    谢谢、

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    但您使用真实的示波器来测量信号吗? 例如、DSO 062等1MHz 带宽 DIY 套件中没有一个? 我将在几小时内在 LaunchPad 上运行该示例、并发布一些屏幕截图以进行比较。

    Dennis
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    在这里、您可以看到我的测量值-它始终是第一个字节0x15。 蓝色表示 SYNC、绿色表示时钟、紫色表示数据。

    1MHz:

    8MHz:

    16MHz:

    当然、16MHz 时钟不如1MHz 时钟好、但绝对没问题。

    Dennis

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我看到、这是可以的、因为 SPI 器件将过滤掉振荡、而不关心上升/下降时间延迟?

    非常感谢、
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    [引用用户="Sahar Elyahoodaya)]我看到了、这是可以的、因为 SPI 器件将过滤掉振荡、并且不关心上升/下降时间延迟? [/报价]

    这一切都与阈值电平有关-通常输入具有施密特触发器、这些触发器对缓慢的上升/下降沿并不关心(但我不会在这里讨论慢速)。 如果振荡将超过阈值电平、您可能会遇到问题、但只要它远离该阈值、一切都很好-这里就是这种情况。

    我还有几张图片给大家-正如我说过的、我的示例在 RX IFG 上触发、与在 TX IFG 上触发相比浪费了一点时间、但我不知道您需要多快的传输。 以下是我的示例中 RX 触发器的图片:

    这大约是12µs。

    您可以看到数据包之间的间隙-如果您将代码更改为在 TX 中断上触发、USCI 状态机不会等到字节已传输后再复制下一个字节。 相反、当 TX 缓冲区空闲时、它会复制下一个字节、而这种情况发生在前一个字节当前传输时。 但是、由于您的 SPI 以 CPU 频率运行、因此您将不会像 SPI 以较低频率运行那样受益。 请记住、进入 ISR 也需要一些 CPU 周期。 在 CPU 频率下、这将是 SPI 的一个以上字节时间。

    我通常倾向于在 RX 上触发、因为您肯定知道最后一个字节的传输时间、并且可以安全地将 CS (在本例中为 SYNC)再次拉高。 当然、您也可以使用 UCBUSY 位。

    但我同意 Bruce 的观点-当 SPI 时钟接近 CPU 的时钟频率时、中断驱动可能不会给您带来好处。 您还可以考虑这样做:

    #include "msp430g2553.h"
    #include "stdint.h"
    
    void adg714_start_transmission (void);
    
    uint8_t adg714_Bytes[4]={0x15、0x25、0x35、0x45};// ADG714器件的随机字节
    
    (void)
    {
    WDTCTL =(WDTWPW void); //停止看门狗定时器
    
    BCSCTL1 = CALBC1_16MHz; //将范围设置为16MHz
    DCOCTL = CALDCO_16MHz; //将 DCO 步长和调制设置为16MHz
    
    P1OUT = 0x02; // P1.1高电平上的 SYNC 将 ADG714 SCLK 和 DIN 缓冲器
    P1DIR 断电 = 0x02; //将 P1.1设定为输出方向
    
    P1SEL = 0x14; //为 UCA0SIMO (P1.2)和 UCA0CLK (P1.4) P1SEL2
    = 0x14设置特殊功能寄存器1; //为 UCA0SIMO (P1.2)和 UCA0CLK (P1.4) UCA0CTL1
    
    = UCSWRST 设置特殊功能寄存器2; //将 USCI 模块设置为复位状态
    UCA0CTL0 =(UCMSB | UCMST | UCSYNC); //时钟空闲、数据在第一个边沿发生变化、在第二个边沿捕获、MSB 在第一个边沿、主器件、同步模式
    UCA0CTL1 |= UCSSEL_2; // SMCLK 作为时钟源
    UCA0BR0=1; //预分频器1
    UCA0BR1 = 0;
    UCA0CTL1 &=~Ω UCSWRST; //从复位
    
    __bis_SR_register (GIE)中释放 USCI 模块; //启用全局中断
    
    while (1) //无限循环
    {
    //...
    
    if (发生什么事) //发送数据的事件触发器
    {
    adg714_start_transmission (); //启动传输
    }
    
    //...
    }
    }
    
    void adg714_start_transmission (void)
    {__BIC_SR_register(
    GIE ); //禁用全局中断
    P1OUT &=~0x02; // SYNC LOW 为 ADG714 SCLK 和 DIN 缓冲器加电
    while (!(IFG2 & UCA0TXIFG)); //等待空闲的 TX 缓冲
    区 UCA0TXBUF = adg714_Bytes[0]; //发送第一个字节
    while (!(IFG2 & UCA0TXIFG)); //等待空闲的 TX 缓冲
    区 UCA0TXBUF = adg714_Bytes[1]; //发送第二个字节
    while (!(IFG2 & UCA0TXIFG)); //等待空闲的 TX 缓冲
    区 UCA0TXBUF = adg714_Bytes[2]; //发送第三个字节
    while (!(IFG2 & UCA0TXIFG)); //等待空闲的 TX 缓冲
    区 UCA0TXBUF = adg714_Bytes[3]; //发送第四个字节
    while (UCA0STAT & UCBUSY); //等待完整的传输
    P1OUT |= 0x02; //同步高电平更新数据
    __bis_SR_register ( GIE ); //启用全局中断
    } 

    这只需大约6µs μ s 即可完成:

    但是、它会在传输期间阻止中断、这样就不会有其他任务发生。 这取决于您的应用、哪种方法最有用。 如果您有一个传输时间、但随后需要最高更新速度、我将使用与最后一个示例类似的内容。 如果有很多传输、但您不需要最快的更新、那么我不会使用一种在大多数运行时阻止其他中断的方法。

    Dennis

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    非常感谢 Dennis。 我的应用可以容忍高达50us 的时间来更新所有32位、因此在 RX 上触发的第一种方法对我来说是可以的。 我将获取一些 ADG714器件并进行测试。 非常感谢您的所有建议。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    请注意:有些中断的优先级高于 USCI 中断-例如所有定时器。 它们将始终首先被处理、因此如果您具有优先级较高的长中断函数(应始终避免)、则传输时间可能会延长。 但我不知道您的其余代码以及您将在哪个间隔内使用哪些中断源。

    Dennis

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    好的、这是有道理的、这正是我所担心的。 我还没有考虑如何将我的其余代码与 SPI 代码放在一起。

    基本而言、其余代码包含 PWM 和 ADC。 根据 ADC 的读数、我确定多路复用器的哪个选择引脚应变为高电平以允许 PWM 通过。 根据 ADC 读数、我还需要确定在给定时间需要打开或关闭 SPI 器件的哪个开关。 那么、我 将具有一个 ADC、PWM 和一个计时器 ISR。

    如前所述、ADC 和计时器中断将进一步延长传输时间。 因此、我实际上在考虑简化设计、然后使用 ADG714数据表图8中所示的实现。 我不会像他们建议的那样使用 ADG739、而是使用多路复用器来消除额外的传输时间。 此实现将传输时间减少4个、但当然、代价是一次只能选择一个芯片。  

    我需要对此进行更多思考、并可能很快会向您提出更具体的问题。

    谢谢:)

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗯、你可以随意问任何问题、但也许你对时间太担心了。 这只是为了基本理解-下面是 MSP430G2553的中断优先级列表:

    如您所见、只有定时器、比较器和看门狗具有更高的优先级(当然、优先级为30和31)。 牢记工作原理总是很好的。 它还表明、具有最关键时序的过程应由 TA1CCR0处理。

    但我想您应该构建您的电路并对其进行一些测试。

    Dennis

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    好的、谢谢。 此图表非常有用。  

    好的、由于我们讨论的是中断主题、请允许我向您澄清一下。  

    在我的另一段代码中、我在主函数中配置了 PWM。 然后、我有一个计时器中断、该中断从 ADC 读取数据并根据读取结果执行任务。 因此、按照我对中断的理解、如果调用了中断、CPU 将暂时将其主任务保留在主函数中以处理中断。 在我的情况下、PWM 应该在短时间内停止、以便 CPU 处理中断、从 ADC 读取并执行基于它的另一个任务、然后返回到生成 PWM。 同样、当我们以 CPU 速度运行 SPI 时钟时、CPU 没有足够的时间离开主函数。 处理 RX 中断并再次返回其主函数。 这就是我看到信号失真的原因。 如果我说的是正确的、是否有一条规则规定中断需要比 CPU 时钟慢多少才能不会造成问题?  

    如果我在这里说的任何错误、请您纠正我的问题吗?  

    非常感谢您在 Dennis 的帮助。 您非常有帮助:)  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    [引用 USER="Sahar Elyahoodaya"]如果调用了中断,CPU 将暂时将其主任务保留在主函数中以处理中断

    是的、这就是它的基本工作方式。 如果处理器当前正在处理另一个中断、则会有所不同-在这种情况下、当前活动的中断将首先完成。 之后、下一个最高优先级中断被处理、或者如果当前没有中断、主任务从中断点开始执行。 您不会丢失任何中断、除非执行一个中断所花费的时间超过另一个中断的间隔时间。

    [引用用户="Sahar Elyahoodaya"]在我的情况下、PWM 应该停止片刻、CPU 才能处理中断、从 ADC 读取并执行基于它的另一个任务、然后返回生成 PWM。

    这取决于您的 PWM - MSP 可以在硬件(OUTMOD_7)中执行 PWM、而无需任何 CPU 时间。 如果您使用硬件 PWM、则不会受到任何影响。

    [引用 USER="Sahar Elyahoodaya"]当我们以 CPU 速度运行 SPI 时钟时,CPU 没有足够的时间离开主函数。

    CPU 将始终离开主函数来处理中断。 如果您的程序仅从中断跳转到中断、而没有时间在主程序中执行其他操作、则会出现问题。 以 CPU 频率运行的 SPI 的问题是、使用中断没有优势。 我认为它可能更慢。 我不知道进入中断函数所需的 CPU 周期的确切数量(必须仔细检查)、但想象一下:假设从上升中断标志到进入 ISR 并开始处理其中的代码需要十个周期。 在 ISR 内部、您现在将一个字节复制到 TX 缓冲区中、这也需要时间、然后离开 ISR、它会再次添加周期。 我们现在可以有10多个字节、但 USCI 模块在硬件中运行、因此一旦数据字节位于传输缓冲区中、将字节移出只需要8个周期。 如果 USCI 模块发出另一个中断标志、前面提到的所有步骤都将再次发生。 因此、SPI 本身比您的 CPU 更快、并且具有所有 ISR 开销、并且传输会因中断事件而出现瓶颈。 在这种情况下、轮询 USCI 标志的示例将会更快。

    [引用用户="Sahar Elyahoodaya"]是否有一条规则规定中断需要运行的速度比 CPU 时钟慢多少[/引用]

    中断永远不会比 CPU 慢-它以 CPU 频率处理。 我认为您是指类似中断应在哪个间隔内发生的情况。 这方面没有任何规则-取决于具体的应用程序。 中断应尽可能短、以便为执行主代码留出足够的时间。 有些任务根本无法在每个处理器上完成。 另一个示例:假设您的处理器具有10MHz 的最大时钟频率、您现在希望每1µs μ s 有一个计时器中断在 ISR 内部执行一些操作-这将是每十个时钟周期产生一次中断。 这不是一个运行在10MHz 上的处理器能够处理的任务-这是完全不可能的。 您需要更快的处理器。

    Dennis

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    很好的解释。 非常感谢 Dennis。 我有几个跟进问题、在我向您提问之前、我正尝试整理这些问题。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    请帮助我了解更多主题:

    如前所述、我有一个代码、在其中我在硬件中生成 PWM、从浮动引脚读取 ADC 以生成5位随机数、并将随机数转换为十进制数。 然后、我想使用该随机数来驱动多路复用器的5个选择引脚、并最终通过 SPI 协议控制某些开关的关闭和打开时序。 但现在让我们不要考虑 SPI 代码、这样我就可以理解这些概念、而不会给自己带来太多复杂的事情。  

    在与您就中断进行上述对话后、我意识到、我可能会将大量代码放入计时器 ISR 中进行处理。 这样做会导致 ISR 在返回主函数之前需要更长时间才能完成。 这导致了我尝试使用计数器在特定持续时间内生成和保持的脉冲被延长。

    我不知道如何在我放入主函数的代码量和 ISR 之间实现平衡。

    我的第一种方法是仅在主代码中生成 PWM。 然后、我转到计时器 ISR 循环并生成随机数、以随机将 P1.0拉至 P1.4高电平。 这种方法有效、但每个引脚保持高电平的时间比我希望的时间长。 正如我说过的、我使用计数器来确定引脚应保持多长时间。 如何确定 ISR 内部的每个计数将需要多长时间?

    我的第二种方法是将代码的某些部分移至主函数。 因此、除了在 main 函数中生成 PWM 之外、我还会从 ADC 中读取并生成十进制的随机数、然后移至 ISR、其中 P1.0至 P1.4将在确定的持续时间内随机拉高。 这是更好的方法吗? 我没有使其工作、因为我不知道如何使代码跳转到 ISR 循环并返回主循环。

    我想其他方法是在时间中断之外使用 ADC 中断。 或者只有 ADC 中断和其他任务在主循环中完成。 但我对如何跳转到中断获取值、跳转到另一个中断然后返回主函数感到困惑。 请帮助我理解。

    以下是我的第一种方法的代码、如果它有助于:

    #include "msp430G2553.h"
    #include 
    
    
    void main (void)
    {
    //停止看门狗计时器
    WDTCTL =(WDTPW | WDTHOLD);
    
    //将范围设置为1MHz
    BCSCTL1 = CALBC1_1MHz;
    //将 DCO 步长和调制设置为1MHz
    DCOCTL = CALDCO_1MHz;
    
    P1SEL |= 0x40; //将 P1.6的特殊功能设置为计时器模块
    P1DIR |= 0x5F; //设置为输出方向
    P2DIR |= 0x0F;
    P1OUT |= 0x00; //设置 P1.0打开、P1.1关闭
    P2OUT |= 0x00;
    
    
    // PWM 频率40kHz (25us)
    TA0CCR0 = 400;
    //从占空比基础
    TA0CCR1 = 60开始;
    //复位/设置模式
    TA0CCTL1 = OUTMOD_7;
    // SMCLK、分频器1、上行模式、清零、 溢出中断使能
    TA0CTL =(tassel_2 | ID_0 | MC_1 | TACLR | TAIE);
    
    
    //启用全局中断
    _ bis_SR_register (GIE);
    
    //无限循环-主程序
    while (1);
    
    }
    
    
    //计时器0 A1中断服务例程
    #pragma vector = TIMER0_ISR_Vector
    _
    (TimerA1_void)
    volatile uint8_t A0; //在此处存储 ADC 位
    volatile uint8_t rn = 11111;//5位随机数;
    volatile uint8_t n = 6; //循环5位
    volatile uint8_t dec = 0; //十进制值0-31
    volatile uint8_t rem; //余数
    volatile uint8_t base = 1; //基极
    静态 uint8_t 标志= 1; //flag
    
    静态 uint8_t 计数器= 5; //计数器
    
    //状态名称
    静态 uint8_t S0 = 0;
    静态 uint8_t S1 = 0;
    静态 uint8_t S2 = 0;
    
    ADC10CTL1 |= INCH_5; //从 P1.5、浮点引脚读取
    ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON;
    ADC10CTL0 |= ENC + ADC10SC;
    
    
    TA0CTL &=~TAIFG; //清除中断标志
    
    
    //生成5位 ADC 并转换为十进制数
    if ((Flag = 1)||(dec = 0))
    {
    
    while (n!= 0)
    {
    
    while (ADC10CTL1 & ADC10BUSY);
    A0 = ADC10MEM & 0x01;
    
    如果(A0)
    {//比较 如果 LSB 为1
    N-;
    RN ^=(1 << n);
    }
    //如果是,则将 LSB = 1保存到左移
    //寄存器 RN
    如果(!A0)则为其他值
    {
    N-;
    RN ^=(0 << n);
    }
    }
    //如果不保存 LSB = 0到左移
    //寄存器 RN
    while (RN >0)
    {
    REM = RN % 10;
    DEC =十进制+ rem * base;
    base = base * 2;
    RN = Rn / 10;
    }
    
    如果(!n)
    {
    N = 6;
    标志= 0;
    }
    }
    
    //pull P1.0 to P1.4 high based on the generated random number and hold for counter numereber of times (根据生成的随机数将 P1.0拉至 P1.4高电平并保持计数器
    
    if ((Flag =0)&&(dec !=0))
    {
    如果(!(-counter)))
    {
    如果(!S0 &&!S1 &&!S2)
    {
    
    P1OUT =十进制;
    P2OUT = 0x0C;
    计数器= 5;
    
    //Flag to Go to next state
    S0 = 0;
    S1 = 1;
    S2 = 0;
    
    }
    
    否则(!S0 && S1 &&!S2)
    {
    
    P1OUT = 0x00;
    P2OUT = 0x00;
    计数器= 5;
    
    S0 = 1;
    S1 = 0;
    S2 = 0;
    }
    
    否则(S0 &&!S1 &&!S2)
    {
    
    P1OUT =十进制;
    P2OUT = 0x05;
    计数器= 5;
    
    
    S0 = 1;
    S1 = 1;
    S2 = 0;
    
    }
    
    否则(S0 && S1 &&!S2)
    {
    
    P1OUT = 0x00;
    P2OUT = 0x00;
    计数器= 5;
    
    
    S0 = 0;
    S1 = 0;
    S2 = 1;
    
    }
    
    否则(!S0 &&!S1 && S2)
    {
    FLAG = 1;
    S0 = 0;
    S1 = 0;
    S2 = 0;
    }
    }
    
    
    
    
    
    
    }