【问题标题】:typecasting char variable into unsigned int将 char 变量类型转换为 unsigned int
【发布时间】:2012-10-02 10:51:57
【问题描述】:

我正在尝试将 char 变量转换为 Unsigned int。我的代码是

char spi(char data) 
{ 
    //Start transmision 
    SPDR = data; 
    //Wait for transmision complete 
    while(!(SPSR & 0x80)); 
    return SPDR; 
} 

unsigned int ReadAd(void) 
{ 
    unsigned int data; 

    ChipSelectAd(1); 
    //Read data 
    CheckStatus();                                            
    spi(0x58);                                                
    data = (spi(0xFF)<< 8);    
    data |= spi(0xFF);         

    return data; 
} 

其实我的问题是 spi 函数返回一个 8 位的字符,所以上面的代码将 char 变量左移 8 位,然后将其分配给一个 16 位的变量,结果总是 0。 为了将数据实际向左移动,我需要先将它们类型转换为 16 位类型变量。我试过这样的

char spi(char data) 
{ 
    //Start transmision 
    SPDR = data; 
    //Wait for transmision complete 
    while(!(SPSR & 0x80)); 
    return SPDR; 
} 

unsigned int ReadAd(void) 
{ 
    unsigned int data; 

    ChipSelectAd(1); 
    //Read data 
    CheckStatus();                                            
    spi(0x58);                                                
    data = (unsigned int)((unsigned char)spi(0xFF)<< 8);    
    data |= (unsigned int)((unsigned char)spi(0xFF));         

    return data; 
} 

void CheckStatus(void)
{
//char adcStatus;
adcStatus = 0xFF;                                           
//Read status
while(!(adcStatus & 0x80))
{
    spi(0x40);
    adcStatus = spi(0xFF);
 }
}

void ChipSelectAd(char s)
{


if(s == 1){
    PORTB.3 = 0;    //Switch on ADC
    //while(PINB.3);  //Wait for chip select pin
}
    else
        PORTB.3 = 1;    //Switch off ADC
 }

它不工作。请建议我必须使用哪个功能。

提前致谢。

【问题讨论】:

  • 您真的从SPDR 中得到任何新数据吗? SPDR 是如何定义的?是不是类似于*(char*)SOME_ADDRESS?如果是,那里有volatile,像这样吗?:*(volatile char*)SOME_ADDRESSSPSR 也是如此。
  • @AlexeyFrunze 是的,它必须定义为 volatile。这些是标准的 SPI 硬件寄存器。
  • @Lundin “必须定义”如“它们已定义”或“应定义”?
  • @AlexeyFrunze 如果您不将它们声明为易失性,您的编译器可能会完全失控并优化所有代码,或者进行模糊的优化。更糟糕的是,一些 MCU 通过读取 SPSR 寄存器来清除它,然后读取数据寄存器。如果代码不这样做,标志将永远不会被清除,程序将锁定,永远等待状态标志。
  • @Lundin 我知道如果没有volatile 会发生什么,这就是我提出这个主题的原因。我只是问你是否确定它们已经用volatile 定义(例如在某些“标准”包含文件中或在编译器中硬编码)或者它们有可能不是以这种方式定义的(例如定义的 OP它们错误地或从某处/其他人那里继承了错误的定义)。

标签: c embedded avr


【解决方案1】:

你的问题不在于演员表。由于整体提升规则,没有它们也一样。

#include <stdio.h>

char spi ( char data )
{
  char SPDR = data;
  return SPDR;
}

unsigned int ReadAd ( void )
{
  unsigned int data;

  data = spi ( 0x81 ) << 8;
  data |= spi ( 0x42 );

  return data;
}

int main ( void )
{
  printf ( "Result %x\n", ReadAd() );
  return 0;
}

这会在 char 是有符号类型的系统上输出 Result ffff8142。要解决真正的问题,请尝试将 spi() 调用的值分配给变量,然后打印它们的值。请同时向我们展示SPDR的声明/定义。

【讨论】:

  • 为了挑剔,spi ( 0x81 ) &lt;&lt; 8 有一个问题。 char 可以有符号或无符号,它是实现定义的。这意味着设置了 MSB 的 'char' 类型将被视为有符号并提升为 int(有符号)。然后代码会产生有符号整数溢出,这是未定义的行为。在有符号整数上使用左移也是未定义的行为。永远不要在任何形式的按位运算中使用有符号整数,不会有任何问题。
  • @Lundin 刚刚查找了 C99。对negative value &lt;&lt; count 无条件未定义这一事实感到惊讶(包括简单加法(以实现移位/乘以 2 的非负幂的效果)不会导致溢出的情况)。谢谢。
  • @AlexeyFrunze 如果结果不能被表示,即如果符号位被覆盖,这只是未定义的行为。
  • @Lundin 嗯。它说“如果 E1 具有无符号类型,则值...如果 E1 具有带符号类型和非负值,并且 E1 * 2^E2 可以在结果类型中表示,那么这就是结果值;否则,行为是不明确的。”使用这种措辞,带负号的 E1 属于 UB 的“否则”条款。
  • @AlexeyFrunze 是的。我的意思是,如果你有一个signed int E1 = 1; 然后做E1 &lt;&lt; 4,那么 1*2^4 is 在结果类型中是可表示的(假设是二进制补码),它是 not 未定义的行为。所以未定义的行为不依赖于类型,它取决于你移位时是否导致溢出。
【解决方案2】:

1) 在嵌入式系统中:摆脱标准整数类型,尤其是默认的 char 类型。如果您有现代编译器,请使用 stdint.h 中的 uint8_t、uint16_t 等。如果你有一个古老的编译器,那么typedef unsigned char uint8_t 等等。如果您没有在嵌入式系统中使用已知大小的无符号类型,那么您就是在要求 bugs bugs bugs。

2) 学习和理解integer promotion rules。令人恐惧的是有多少程序员不了解或不了解它们。

当您整理好上述两个基础知识后,您的代码应该类似于以下代码:

(我冒昧地修复了各种可疑的、可能的错误和蹩脚的、不一致的缩进。加上一些风格挑剔。)

#include <stdint.h>

uint8_t spi (uint8_t data) 
{ 
  SPDR = data; //Start transmision 

  while((SPSR & 0x80) > 0) //Wait for transmision complete 
    ; 

  return SPDR; 
} 

uint16_t ReadAd(void) 
{ 
  uint16_t data; 

  ChipSelectAd(true); 

  CheckStatus(); //Read data 
  (void) spi(0x58);                                                

  data  = ((uint16_t)spi(0xFF)) << 8;
  data |=  (uint16_t)spi(0xFF);

  return data; 
} 

void CheckStatus(void)
{
  uint8_t adcStatus;

  do
  {
    (void) spi(0x40);
    adcStatus = spi(0xFF);
  } while((adcStatus & 0x80) > 0);
}

void ChipSelectAd(bool on)
{
  if(on)
  {
    PORTB.3 = 0;    //Switch on ADC
  }
  else
  {
    PORTB.3 = 1;    //Switch off ADC
  }
}

【讨论】:

  • 我仍然建议放弃 uint16_t 演员表。它们是无用的杂物。
  • 我还要说普通 char 类型的使用非常合理:当用于字符串和字符串函数时。使用指向普通字符的指针是避免为期望普通字符的函数强制转换指针的唯一方法(因为提供 ptr-to-(un)-signed-char 将需要没有强制类型转换的诊断)。
  • @Jens 我已经尝试过你的方式,一旦我的代码有什么问题,你能建议我吗?我是控制器新手,请建议我。
  • @Jens 关于演员阵容:假设您知道 spi() 总是返回一个大的正值并且有array[spi(0xFF) + negative],其中negative 是一个带有负值的普通int。由于您没有跟踪您的类型,因此该代码将崩溃并烧毁。因为你有 uint8 - int,然后是整数提升,uint16 - int,然后是平衡 uint16 - uint16。数组远远超出范围。如果你在那里有一个明确的类型转换,那么你在写那行时可能会更加小心。
  • @Jens 关于 char 类型:这显然是通过 SPI 传输的原始二进制文件。 char 类型在字符串中使用时可能有目的,但我对此表示怀疑。像ch - '0' 这样的语句很危险,因为您最终可能会得到一个签名的结果,然后您将其传递给一些按位运算并调用未定义的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
  • 2011-12-15
  • 2015-01-20
相关资源
最近更新 更多