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.

[参考译文] RTOS/TI-RTOS-MCU:RTOS 延迟 TM4C1294NCPDT

Guru**** 2465890 points


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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/665178/rtos-ti-rtos-mcu-rtos-latency-tm4c1294ncpdt

器件型号:TI-RTOS-MCU

工具/软件:TI-RTOS

您好!

我正在使用 MCU SPI 从从器件读取数据。 从器件通过触发外部中断来标记已准备好提供数据的器件。 在中断中、我发布一个信号量来解锁 SPI_transmission。 当我以低带宽对其进行测试时、它工作正常。 最后、我的从器件以31.3kHz 的频率触发中断、我需要读取2个16位帧。 当我以该频率运行时、从器件触发信号和执行 SPI 任务之间的延迟开始过大。 在外部中断例程中、我只需切换一个引脚(仅供参考)并布置 SPI 信标。 在下面的示波器屏幕截图中、蓝色信号是一个参考信号、一旦代码跳转到 drdy ()例程、该信号就应该被切换、绿色信号是从器件的触发器(下降沿是 callign drdy ())。存在~4us 延迟。 当您查看紫色波形时、延迟甚至更大、紫色波形是 SPI 的信号之一(从器件使能信号)。  

我正在使用两个任务和一个 Hwi。 一个任务没有 while ()循环,只会在请求时运行以配置从属设备(DAC_conf ())。  另一个任务 masterTaskFxn()是一个 SPI 传输任务。 我可以在 ROV 中看到、执行初始化后、第一个任务就会终止。 然后、只有 idle()、 masterTaskFxn()和 hwi 看起来是活跃的。 这种明显的延迟是由 RTOS 调度  程序本身引入的延迟造成的、还是我可以做得更好?

void hardware_init (void)
{

// SysCtlPeripheralEnable (SYSCTL_Periph_GPIOB);
//启用计时器2
//
// Auxilaty SPI 引脚
//
GPIOPinTypeGPIOOutput (GPIO_PORTB_BASE、GPIO_PIN_5);//初始化 PF4作为输入
GPIOPinWrite (GPIO_PORTB_BASE、GPIO_PIN_5、32);//将 PF4作为输入
GPIOPinTypeGPIOOutput (GPIO_PORTB_BASE、GPIO_PIN_3);//初始化 PF4作为输入
GPIOPinWrite (GPIO_PORTB_BASE、GPIO_PIN_3、8);//将 PF4作为输入
GPIOPinTypeGPIOOutput (GPIO_PORTB_BASE、GPIO_PIN_2);//初始化 PF4作为输入
GPIOPinWrite (GPIO_PORTB_BASE、GPIO_PIN_3、4);//将 PF4作为输入
}
//
*=== GPIO/DRDY HWI ===
//
void drdy (unsigned int index)
//void drdy (void)
{
// semaphore_post (tcp_semaphore);
GPIO_TOGGLE (Board_test);//将 PF4作为输入
Semaphore_post (SPI_Semaphore);

}

/*
====== SPI 测量传输任务====
//
void masterTaskFxn (UARg arg0、UARg arg1)
{
SPI_Handle masterSpi;
SPI_Transaction masterTransaction;
SPI_Params spiParams;
bool transferOK;

SPI_Params_init (&spiParams);
spiParams.transferMode = SPI_MODE_BLOCKING;
spiParams.transferTimeout = SPI_WAIT_FOREVER;
spiParams.transferCallbackFxn =空;
spiParams.mode = SPI_MASTER;
spiParams.bitrate = 10000000;
spiParams.dataSize = 16;
spiParams.frameFormat=SPI_POL1_PHA1;

Semaphore_pend (SPI_Semaphore、BIOS_wait_forever);

/*将 SPI 句柄初始化为默认主控*//
masterSpi = SPI_open (0、&spiParams);
masterSpi = SPI_open (Board_SPI0、&spiParams);
masterTxBuffer2[0]=0b0001000000000;
masterTxBuffer2[1]=0b0000000000000000;
masterTxBuffer[0]= 0b00010010;

/*启用中断*/
GPIO_enableInt (Board_BUTTON0);

while (1)
{
Semaphore_pend (SPI_Semaphore、BIOS_wait_forever);
/*初始化主 SPI 事务结构*/
ctr1=ctr1+1;
masterTransaction.count = 1;//事务的帧数
masterTransaction.txBuf =(ptr) masterTxBuffer;
masterTransaction.rxBuf =(ptr) masterRxBuffer;
/*启动 SPI 传输*/
transferOK = SPI_transfer (masterSpi、&masterTransaction);

// if (transferOK){
// /*主接收缓冲区的打印内容*//
System_printf ("主设备:%s\n"、masterRxBuffer);
//}
//否则{
// System_printf ("主器件 SPI 传输失败");
//}


/*取消初始化 SPI */
SPI_CLOSE (masterSpi);

System_printf ("done");

system_flush();
}/*



==== 配置任务====
*/
void DAC_conf (UArg0、UArg0 arg1)
{

SPI_Handle masterSpi_DAC;
SPI_Transaction masterTransaction_DAC;
SPI_Transaction masterTransaction_s2p;
SPI_Transaction masterTransaction_ADC;
SPI_Params spiParams_DAC;
bool transferOK;

SPI_Params_init (&spiParams_DAC);
spiParams_dac.transferMode = SPI_MODE_BLOCKING;
spiParams_dac.transferTimeout = SPI_WAIT_FOREVER;
spiParams_dac.transferCallbackFxn =空;
spiParams_dac.mode = SPI_MASTER;
spiParams_dac.bitrate = 5000000;
spiParams_dac.dataSize = 16;
spiParams_dac.frameFormat=SPI_POL1_PHA1;

// Semaphore_pend (SPI_Semaphore_DAC、BIOS_wait_forever);
/*将 SPI 句柄初始化为默认主控*/
masterSpi_DAC = SPI_open (0、&spiParams_DAC);
// masterSpi = SPI_open (Board_SPI0、NULL);

/*初始化主 SPI 事务结构*/
masterTransaction_dac.count = 1;//事务的帧数
masterTransaction_dac.txBuf =(ptr) dac_Buffer;
masterTransaction_dac.rxBuf =(ptr) masterRxBuffer;

GPIOPinWrite (GPIO_PORTB_BASE、GPIO_INT_PIN_5、0);//初始化 PF4作为输入
/*启动 SPI 传输*/
transferOK = SPI_transfer (masterSpi_DAC、&masterTransaction_DAC);
GPIOPinWrite (GPIO_PORTB_BASE、GPIO_INT_PIN_5、32);//初始化 PF4作为输入
DAC=1;

// Semaphore_pend (SPI_Semaphore_DAC、BIOS_wait_forever);
/*初始化主 SPI 事务结构*/
masterTransaction_s2p.count = 1;//事务的帧数
masterTransaction_s2p.txBuf =(ptr) S2P_Buffer;
masterTransaction_s2p.rxBuf =(ptr) masterRxBuffer;

GPIOPinWrite (GPIO_PORTB_BASE、GPIO_INT_PIN_3、0);//初始化 PF4作为输入
/*启动 SPI 传输*/
transferOK = SPI_transfer (masterSpi_DAC、&masterTransaction_s2p);
GPIOPinWrite (GPIO_PORTB_BASE、GPIO_INT_PIN_3、8);//初始化 PF4作为输入
DAC=2;

// Semaphore_pend (SPI_Semaphore_DAC、BIOS_wait_forever);
GPIOPinConfigure (GPIO_PD2_SSI2FSS);
GPIOPinTypeSSI (GPIO_PORTD_base、GPIO_PIN_0 | GPIO_PIN_1 |
GPIO_PIN_2 | GPIO_PIN_3);

masterTxBuffer[0]= 0b0100000100000001;
/*初始化主 SPI 事务结构*/
masterTransaction_adc.count = 1;//事务的帧数
masterTransaction_adc.txBuf =(ptr) masterTxBuffer;
masterTransaction_adc.rxBuf =(ptr) masterRxBuffer;
transferOK = SPI_transfer (masterSpi_DAC、&masterTransaction_ADC);
DAC=3;

// Semaphore_pend (SPI_Semaphore_DAC、BIOS_wait_forever);
masterTxBuffer[0]= 0b00001000;
/*初始化主 SPI 事务结构*/
masterTransaction_adc.count = 1;//事务的帧数
masterTransaction_adc.txBuf =(ptr) masterTxBuffer;
masterTransaction_adc.rxBuf =(ptr) masterRxBuffer;
transferOK = SPI_transfer (masterSpi_DAC、&masterTransaction_ADC);
DAC=4;

/*取消初始化 SPI */
SPI_Close (masterSpi_DAC);
/*打开将在循环中发送测量值的新 SPI 连接*/
Semaphore_post (SPI_Semaphore);
/*启用中断*/
GPIO_enableInt (Board_BUTTON0);
// GPIOIntEnable (GPIO_PORTB_BASE、GPIO_PIN_4);

if (transferOK){
/*打印主接收缓冲区的内容*/
System_printf ("配置完成\n");
}
否则{
System_printf ("主 SPI 传输失败");
}
system_flush();
}/*



==== main ====
*/
int main (void)
{

/*构造 BIOS 对象*/
Task_Params taskParams2;
Task_Params 任务参数3;

/*呼叫板初始化函数*/
Board_initGeneral();
Board_initGPIO();
Board_initSPI();
SysCtlPeripheralEnable (SYSCTL_Periph_GPIOD);
GPIOPinTypeGPIOOutput (GPIO_PORTD_base、GPIO_PIN_2);//初始化 PF4作为输入
GPIOPinWrite (GPIO_PORTD_BASE、GPIO_PIN_2、4);//将 PF4作为输入

hardware_init();

/*安装按钮回调*/
GPIO_setCallback (Board_BUTTON0、drdy);

////启用中断*//
GPIO_enableInt (Board_BUTTON0);


//构造主线程*
Task_Params_init (&taskParams2);
taskParams2.priority = 2;
taskParams2.STACKSIZE = TASKSTACKSIZE;
taskParams2.stack =_task0Stack;
Task_construction (&task0Struct,(Task_FuncPtr) masterTaskFxn、&taskParams2, NULL);
//构造主 DAC 任务线程
Task_Params_init (&taskParams3);
taskParams3.priority = 1;
taskParams3.STACKSIZE = TASKSTACKSIZE;
taskParams3.stack =&task1stack;
Task_construct(&task1Struct、(Task_FuncPtr) DAC_conf、&taskParams3、NULL);


/*启动 BIOS */
BIOS_start();

返回(0);
} 

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

    System_printf 数据存储在内部缓冲区中。 但是、System_flush 会将此数据写入 CIO 缓冲器。 当数据中有'\n'时、目标会停止、以便 CCS 可以读取数据。 这通常会使实时操作混乱。 如果要打印的字符串较长、情况会更糟。 请尝试删除 System_flush、然后使用 Tools->ROV->SysMin 查看 System_printf 输出并查看这是否会产生影响。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Charles、

    我已对所有 Sysyme_flush()实例进行了注释。 不幸的是、它没有改善。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    在非 RTOS 配置的情况下、一旦从器件指示其状态、基准切换需要~500-600ns。 使用 RTOS、我将获得4-6us。 ~32kHz 速率上的32位应该更多地作为非 RTOS 解决方案?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    如果您询问"是否在不使用 RTOS 的情况下运行"减少中断延迟"-然后我会-无任何疑问-回答"是!"
    从存储器-当 MCU 运行时、"进入 ISR 在14个系统时钟内发生"、"从 RTOS 中释放"。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    谢谢你。

    我的问题更多:根据您的经验、我正在使用的应用程序对于 RTOS 似乎是实用的?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我对您的应用没有经验- (几乎)公司/我绝不会雇用 "供应商无关"的 IDE 或 RTOS 以外的人员。    为"如此有限的源代码实施"投入大量时间/精力超出了我们的客户、投资者和我们的团队的兴趣/舒适程度。    (请注意、ARM Cortex 具有丰富的 M0、M3、M4和 M7 -有许多供应商-我无法用什么方式描述"一个和唯一一个"可以(自愿)、 "锁定在上面!")

    我不确定您是否正确地总结了您的整个应用、并进行了"加权"、以便判断这种"实用性"。   正如您所知-在这个领域中很少是"免费的"- RTOS 提供的任何优势都是以(部分)运营成本获得的。   在您的情况下-中断响应时间已降级-您是此类影响的最佳判断者...

    而不  是"我的判断"-是否"您的测试"-证明"最适合您的测量判断?"    您的"RTOS 的替代产品"是否会与 MCU 的"SysTick "一起使用-有可能实现一个"侵扰度更低-惩罚更少的实时系统"-并且大大改进了中断响应?    这至少是一项授权(甚至)一项简短(衡量的)调查--是否不是?

    Cortex M3 (通过 LM3S、此处)和 M4 (通过 LX4F 和现在的 TM4C)可能会实现12个周期的中断延迟、但这(仅限)具有 "零等待状态存储器系统"。   

    ARM Cortex 的其他优势包括"中断嵌套"-"优先级划分"-和(甚至)"挤占"-这会将中断传递到"顶犬"-确保以最小的"抖动"为其提供服务-与 MCU 的当前状态无关...  (即完全停止任何"正在进行中的中断"-完成其服务并退出...然后才允许"中断的 ISR"继续...)

    小世界部:(如果您的学校是 MIT -十年+过去 -公司/我为"等离子融合 Gyrotron"提供了设计和产品-同时雇用了@ MIT 和 Princeton。)

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

    "存在~4us 延迟。"

    这是300 - 500条指令、提供或接受。 考虑到 RTOS 开销、时间有点长、但还不错。

    我通常要做的是设置测试运行、以了解开销大小。 将一个闪烁任务和几个空循环设置为一个任务。 看看您的闪烁速度有多快。 然后、全部裸机运行相同的闪烁代码并比较速度损失。

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

    2月17日18:13的帖子如下:

    [引用 USER="Lukasz Huchel">如果是非 RTOS 配置、则从器件在状态标志时需要~500-600ns 的基准切换。  使用 RTOS、我将获得4-6us。 [/报价]

    当 MCU 以启用的时钟速率运行(并且在存储器区域中)时、 "0 wait state"-中断延迟为12个时钟。   (如果是'123-运行40MHz SysClock - 300nS 中断延迟结果。)    因此、您的"非 RTOS "操作可能会遇到"等待状态"。

    该差值(300nS 与4 6µS ... 峰值@ 20:1)可能会也可能不会明显-这是由您和您的应用决定的。

    是(仅) RTOS 造成的问题吗?   如果是、您是否已经探索(其他) RTOS 实现、以了解它们的比较方式?   这是(适当的)调查工作-技术人员通常需要-是不是吗?

    另一条建议-"采用闪烁"- iirc 避免中断-因此可能不会呈现 "同类比较"。   (即提供"部分"、但低于"完全/正确"的比较见解...)

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

    尊敬的 Lukasz:

    请查看 TI-RTOS 中的 SYS/BIOS 版本说明。 您将找到指向计时基准的链接。 我们记录了几种基本的时序方案。 例如:

     

    《SYS/BIOS 用户指南》中还有一个章节、详细介绍了每一行的含义。

    我将确保您使用的是未检测的 TI-RTOS 驱动程序库和自定义 BIOS 库、而不启用日志记录。 我通常在内核中将断言保留到最后 内核中的断言检查开销不大、值得更快地找到试点错误(例如将无效参数传递到 API 中)。

    您仍然可以在应用程序中有一个不受内核管理的中断(我们将其称为零延迟中断、因为内核的延迟为零)。 需要注意的是、如果 ISR 可以更改调度程序、则它无法调用内核(例如、无法调用 semaphore_post)。

    Todd

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

    虽然此图表很有用-但忽略了两个引用(1和2)。

    ARM 在12个周期中列出"中断延迟"、但仅当"等待状态存储器0 "处于"运行状态时才会列出"中断延迟"。   怀疑(缺失)参考(2)会"回显""0等待状态存储器"要求。   此图表显示了通过"0等待状态内存"获得的(仅限)性能。

    需要注意的是、135/12比此 RTOS 造成的延迟损失"11倍"更好...

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    请参阅我指向的文档以了解完整说明。 我没有在表格和脚注中添加整页内容、以最大限度地缩短线程长度。