使用HAL库和stm32cubemx硬件I2C控制DAC6573各通道输出

DAC6573是德州仪器一款有10位精度的四通道数模转换器,采用I2C方式通信。本文用STM32RCT6的I2C1实现控制该芯片四路分别输出给定的电压值,其中使用了目前尚还有些问题的HAL库实现硬件I2C通信。

DAC6573使用与工程大体思路

DAC6573设置方式为用I2C向器件写入四个连续的字节,分别为Address byte,Control byte,MS-Byte,LS-Byte。
Address byte:本示例中为0x98。它表示部分器件地址和读写模式,这里采用最常用的写入模式而不是Broadcast模式,因此该字节高5位为固定的10011,6、7位按A1,A0连接情况而定,本实例中都选择了接地,因此都为0。第八位表示读写模式,0表示写入。综上,Address byte为10011 00 0,即0x98。
Control byte:本示例中对四个通道进行操作对应的Control byte依次为0x18、0x1A、0x1C、0x1E。Control byte的高两位和Address byte的6、7位一样是可改变的地址位,本示例中A2、A3仍采用直接接地,因此这两位仍为0。第3、4位(即L1、L0)表示模式,这里使用最常用的模式,即对指定通道写入,写入后立即改变该通道的输出电压,对应的该两位为01。第5位无意义,可任取,这里取1。6、7位是通道选择,00、01、10、11分别对应CHANNEL A、B、C、D。最低位为模式选择,这里选择0,选择1则会进入Power-down模式。因此对四个通道进行操作对应的Control byte依次为0x18、0x1A、0x1C、0x1E。
MS-Byte和LS-Byte则用于发送十位数据,MS-Byte的8位即为十位数据的高8位,LS-Byte的高2位为十位数据的最低2位,低6位则无意义。因此对十位数据进行左移或者右移即可得到这两个字节的值。十位数据计算方式如下:
在这里插入图片描述

将这四个字节依次用I2C发出即可控制DAC6573任一通道的输出值。下面就是具体操作了。

stm32cubemx配置

cube中启用与DAC6573相连的I2C外设,I2C配置使用默认参数即可。其他配置按正常工程配置即可,这里不加赘述。

关于HAL库实现硬件I2C的问题

cube生成工程直接打开,其中的代码在实现硬件I2C上仍有一个问题,修正后即可正常使用,具体如下:

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hi2c->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */
    __HAL_RCC_I2C1_CLK_ENABLE();//这句原来在下面,需要剪切到这里
  /* USER CODE END I2C1_MspInit 0 */
  
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Peripheral clock enable */
    //__HAL_RCC_I2C1_CLK_ENABLE();原来cube生成的代码这一句在这个位置,需剪切至上面
  /* USER CODE BEGIN I2C1_MspInit 1 */

  /* USER CODE END I2C1_MspInit 1 */
  }
}

注:这个初始化函数位于stm32f1xx_hal_msp.c*中,每次用cube生成代码后都需要手动修改

具体代码实现

最后一步就是把前面的思路转化成代码了。下面是用于I2C主机发送的函数:

HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)

四个参数分别为句柄、器件(DAC6573)地址、发送数据指针、发送数据大小、超时时间。Address byte输入至第二个参数即可自动在其余数据前发送,因此操作这个函数只需要发送后三个字节既可以了。

在main.c靠前的地方加上如下宏定义和变量初始化部分,便于后期操作:

#define CHANNEL_A 5
#define CHANNEL_B 6
#define CHANNEL_C 7
#define CHANNEL_D 8
#define DAC6573_VREF_H 5000//由外部电路控制,按照实际情况修改,单位与实现函数内的uint16_t target_Voltage统一,本示例中该引脚接5000mV
#define DAC6573_VREF_L 0//由外部电路控制,按照实际情况修改,单位与实现函数内的uint16_t target_Voltage统一,本示例中该引脚接地
uint8_t I2C1_tx_buff[3] = {0};

最后实现DAC6573控制的函数如下:

/* USER CODE BEGIN 4 */
uint8_t DAC6573_Set_Voltage(uint8_t channel,uint16_t target_Voltage)
{
	switch (channel)
	{
		case CHANNEL_A :
			I2C1_tx_buff[0] = 0x18;//I2C1_tx_buff[0]即为Control byte
			break;
		
		case CHANNEL_B :
			I2C1_tx_buff[0] = 0x1A;
			break;
		
		case CHANNEL_C :
			I2C1_tx_buff[0] = 0x1C;
			break;
		
		case CHANNEL_D :
			I2C1_tx_buff[0] = 0x1E;
			break;
		
		default :
			return 1;
	}
	uint16_t D = (uint16_t) ( (target_Voltage - (2 * DAC6573_VREF_L)) * 1024 / (DAC6573_VREF_H - DAC6573_VREF_L) );//根据前面图中的计算公式推出该十位数据的计算式
	if ( D > 1023 ) { D = 1023;}
	I2C1_tx_buff[1] = D >> 2;//I2C1_tx_buff[1]即为MS-Byte
	I2C1_tx_buff[2] = D << 8;//I2C1_tx_buff[2]即为LS-Byte
	HAL_I2C_Master_Transmit(&hi2c1,0x98,I2C1_tx_buff,3,1000);//实际上发送了4个字节
	return 0;
}
/* USER CODE END 4 */

这个函数就可以实现控制DAC6573的各通道输出了
后期对返回值进行各种定义并对函数进行部分修改,即可让返回值代表此次修改的各种状态(如参数不合法、修改失败等)

使用示例:

DAC6573_Set_Voltage(CHANNEL_A,3000);

将A通道输出电压设置为3000mV。第二个参数为想要第一个参数指定的通道输出的目标电压,单位与宏定义中DAC6573_VREF_H、DAC6573_VREF_L的单位保持一致且大小介于两者之间即可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值