我对指针及其使用有一般的了解,但我遇到了一些使用指针转换的代码(我想),我从未见过,也不确定我是否理解

该代码是一个C语言函数(为STM 32 F091微控制器编写),用于从只读存储器(93c66)读取数据.

据我所知,该函数将地址视为参数.地址值用作指针并在SPI上发送.

如果通信正确完成,它将开始监听从EEPROME发送的实际值,然后该函数返回数据

unsigned short rd_word(unsigned short add93)
{
  unsigned char *pt = (unsigned char *)(&add93);
  unsigned char error = 0;
  
  CS_HIGH;
  
  add93 &= 0x00ff;
  add93 |= 0x0600;
  
  while (HAL_SPI_Transmit(&hspi2, pt, 1, 1000) != HAL_OK){
    __iar_builtin_no_operation();
  }

  SET_BIT((&hspi2)->Instance->CR1, SPI_CR1_CPHA);
  
  while (HAL_SPI_Receive(&hspi2, pt, 1, 1000) != HAL_OK){
    __iar_builtin_no_operation();
  }
  
  CLEAR_BIT((&hspi2)->Instance->CR1, SPI_CR1_CPHA);

  
  CS_LOW;
  
  return add93;
}

我的问题是代码unsigned char *pt = (unsigned char *)(&add93);有什么作用?据我所知,它将add93投射到unsigned char指针?

现在,如果unsigned char *pt是一个指针,那么它的值是存储add93的实际内存值,为什么它传输的是pt而不是add93

代码

add93 &= 0x00ff;
add93 |= 0x0600;

add93转换为未签名字符(add93 &= 0x00ff;)并附加收件箱的初始读代码(add93 |= 0x0600;)[0x6 = 110b]

SPI接口设置

/* SPI2 init function */
void MX_SPI2_Init(unsigned int dataSize)
{

  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 7;
  hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

推荐答案

指针大多数时候相当于地址.但在嵌入式系统中,它们通常是微控制器本身内部的本地地址.这里您正在处理某种外部存储器.

rd_word是连接此内存的驱动程序的功能之一.显然,他们决定它应该有一个与该存储器中的地址相对应的参数unsigned short add93,但在您的微控制器中,它被表示为一个普通数字.

在这里,我们可能会注意到,默认使用C附带的unsigned short和类似的"原始数据类型"是不好的做法,尤其是在嵌入式系统中.因为我们不知道这个类型有多大--也许是16位.专业软件中的更好做法是使用stdint.h中的类型,例如uint16_t.

该代码希望将add93地址转换为对内存硬件有意义的内容并通过SPI发送.而且SPI几乎总是逐字节工作,一次8位.因此,我们需要将serializeadd93分成字节块才能发送它.

C有一个特殊的规则,允许我们逐个字节判断任何变量类型.我们可以通过将任何变量的address转换为指向character type(C的字节类型)的指针来做到这一点.最常表示为unsigned charuint8_t,因为在处理原始字节时,我们不希望任何符号把事情搞砸.

为此,&add93获取局部变量add93的地址.该变量位于微控制器的本地堆栈上.通过将地址转换为指向字符(unsigned char *)(&add93)的指针,我们可以获得第一个字节的地址.这里有一些值得注意的事情:

  • 内括号(&add93)不是必需的.
  • 生成的指针将指向unsigned short的最低地址.是否对应于最高有效字节或最低有效字节取决于中央处理器字节顺序(What is CPU endianness?).Cortex M和STM 32几乎完全是小端序,因此我们这里有最低有效字节.
  • 了解字节顺序是嵌入式系统编程的基础.所有通信协议和外围硬件(例如存储器)可能使用与您的中央处理器不同的字节顺序.例如,对于网络协议来说,大数位非常常见.如果我们使用不同的字节顺序与网络或设备交互,我们必须重新洗牌字节顺序.

这里的东西只是一些逐位算术:

add93 &= 0x00ff;
add93 |= 0x0600;

第一行对屏蔽进行逐位AND,所有1均设置为0xFF.称为"掩蔽",这意味着在这种情况下,最低字节被保留,最高字节被清除.然后他们将值0x 6添加到最高字节.我们可能记得,pt点位于最低字节,因此它所指向的值对应于位屏蔽保留的部分.

SPI发送函数HAL_SPI_Transmit(&hspi2, pt, 1, 1000)应该在这里发送1个单字节,即我们指出的那个.

无论出于何种原因,SET_BIT((&hspi2)->Instance->CR1, SPI_CR1_CPHA);都会动态改变SPI通信的时钟相.在传输过程中这样做很奇怪,但那里有很多奇怪的SPI接口,标准化程度很差.

然后它接收1个字节,据称该字节会覆盖pt所指向的数据.这也有点奇怪--我们可能记得,SPI是全速的,总是在接收时发送.因此,如果我们先发送一些东西,然后接收一些东西,我们实际上发送2个字节并接收2个字节.我不知道这里的SPI驱动程序的详细信息,也不知道为什么我们(不)想发送2个字节.

然而,我们可能会注意到,由add93 |= 0x0600设置的最高有效字节尚未发送.这很奇怪.相反,该函数将返回0x 06和从SPI接收的任何内容的混合.

使用内窥镜查看实际发送的数据是strongly recommended.触发芯片 Select 信号并查看MOSI和/或MISO.


要解决您的具体问题:

我的问题是代码united char *pt =据我所知,它将add 93转换为未签名的char指针?

不,如上所述,它将add93中的address转换为字节指针,以便我们可以逐个字节访问unsigned short字节.这是微控制器中的一个地址,不要与您正在处理的任何只读存储器部件的地址混淆.

现在,如果无符号char *pt是一个指针,那么它的值是存储add 93的actula内存值,为什么它传输的是pt而不是add 93?

因为SPI驱动程序的功能类似于HAL_SPI_Transmit,预计有一个指向已分配缓冲区的字节指针作为输入.它需要逐字节工作.他们也可以将(unsigned char*)&add93传递给该功能,这将是等效的.但他们不能仅仅通过&add93,因为那会导致unsigned short*,而unsigned short*unsigned char*不兼容.

与流行的观点相反,C中并不真正允许疯狂和疯狂的指针转换,并且正确地进行它们很棘手.对初学者的一般建议是几乎永远不要选演员,因为它很容易出错.

将add 93转换为未签名字符

不,任何地方都没有强制转换,这直接作用于无符号的短变量.

附加收件箱的初始读代码(add 93| = 0x 0600;

这似乎是目的,但看起来该部分根本没有通过SPI发送过.此代码写得不好,而且可能存在很多错误.也许他们打算在某个时候增加pt以指向下一个字节,但看起来并不像.我想说,代码只是完全损坏了,但我必须仔细看看SPI驱动程序才能确定.

C++相关问答推荐

如何从TPS特定的TGPT_PUBLIC数据 struct 中以OpenSSL的EVP_PKEY

C限制限定符是否可以通过指针传递?

librsvg rsvg_handle_get_dimensions获取像素大小与浏览器中的渲染大小没有不同

C中空终止符后面的数字?

错误:在.h程序中重新定义 struct

如何将FileFilter添加到FileDialog GTK 4

POSIX文件描述符位置

这是一个合法的C Strdup函数吗?

致命:ThreadSaniizer:在Linux内核6.6+上运行时意外的内存映射

每个 struct 变量在C中都有自己的命名空间吗?

Vcpkg的配置文件

C I/O:在Windows控制台上处理键盘输入

这段代码用于在C中以相反的顺序打印数组,但它不起作用

无法识别C编程语言的语法,如书中所示

为什么Linux无法映射这个PT_LOAD ELF段?

使用正则表达式获取字符串中标记的开始和结束

与外部SPI闪存通信时是否应禁用中断?

C中2个数字的加法 - 简单的人类方法

memcmp 是否保证按顺序比较字节?

SSE 向量与 Epsilon 的比较