最近使用到DAC芯片,目的是通过stm32f4控制板控制该芯片输出想要的电压,芯片的通信接口是SPI,一次需要发送24bits的数据才行,但是stm32f4的SPI硬件只有8bits和16bits两种选择,所以没法用硬件来实现,只能通过软件SPI模拟口来实现。
SPI通信接口主要包括4根引线,即MOSI,MISO,SCLK,CS。MOSI是主设备输出从设备输入,MISO是从设备输出主设备输入,SCLK是时钟线,用于同步时钟,而CS是片选信号,用于选择通讯设备。SPI通信中必须包含至少一台主设备,且只有主设备才能发起通讯,而允许存在多台从设备,对于存在多台从设备的情况,可通过从设备的CS引脚选择从设备。
SPI通讯是由时钟线发起的,且只有主设备才能控制时钟线电平的高低,在STM32F4中,当设置CPOL=1时,表示时钟线处于高电平时是空闲状态;CPOL=0时,表示时钟线处于低电平时为空闲状态。当设置CPHA=0时,表示在时钟第一个跳变沿的时候开始采样,而当CPHA=1时,表示在时钟第二个跳变沿时才开始采样。
总的来说,SPI的通讯过程是这样的,以低电平为空闲状态为例,且在第一个跳变沿开始采样(硬件SPI需要设置这两个设定,但软件SPI就不需要,软件SPI的话,主要是看你从设备,也就是DAC芯片是什么样设置,DAC芯片手册如何要求就如何写即可)。我使用的芯片就是低电平空闲,且在第一个跳变沿开始采样。
我的SPI任意使用了PB10,PB11,PB12,PB13四个接口,于是
PB15 ——推挽上拉电阻输出, 对应MOSI
PB14 ——开漏输入, 对应MISO
PB13 ——推挽上拉电阻输出, 对应SCLK
PB12 ——推挽上拉电阻输出, 对应CS
主设备和从设备之间是通过SCLK线来同步消息的,在SCLK线低电平时表示空闲,而在高电平时,从设备会检测MOSI线路上的电平,高低电平表示要发送的数据,因此在SCLK变为高电平时,需要MOSI线上的电平保持稳定。而MOSI的电平由主设备来控制。因此,假设我们要发一个24bits的数据,例如0xffffff,则第一步需要将SCLK拉低,stm32f4中用函数GPIO_ResetBits(GPIOB,GPIO_Pin_13),第二步查看要发送的首位数据是1还是0,0xffffff最高位是1,因此接下来要发送的是高电平,可用GPIO_SetBits(GPIOB,GPIO_Pin_15),将MOSI置高,第三步,拉高SCLK线,从设备接收数据,就是GPIO_SetBits(GPIOB,GPIO_Pin_13),延迟2us,再将SCLK线拉低,这样就实现了一位数据的传输。下面给一个具体的代码。
void SPI2_Init(void) //IO口设置
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure1;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_14;//PB14输入
GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_IN;//普通输入功能
GPIO_InitStructure1.GPIO_OType = GPIO_OType_OD;//开漏
GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure1.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure1);//初始化
//GPIOFB13,12,15初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_15;//PB12,13,15复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
}
void SPI_Send(u32 data) //与DAC通信,dac发送数据是固定24bits的
{
int i=0;
GPIO_ResetBits(GPIOB,GPIO_Pin_12); //CS线,拉低选中从设备
for(i=0;i<24;i++)
{
if(data&0x00800000) //取数据的最高位,一般是从最高位开始发
{
GPIO_SetBits(GPIOB,GPIO_Pin_15); //如果是1,将MOSI拉高
}
else
{
GPIO_ResetBits(GPIOB,GPIO_Pin_15); //如果是0,将MOSI拉低
}
GPIO_SetBits(GPIOB,GPIO_Pin_13); //拉高SCLK线
delay_us(2);
GPIO_ResetBits(GPIOB,GPIO_Pin_13); //拉低SCLK线
data=data<<1; //向左移位,发送下一位数据
}
GPIO_SetBits(GPIOB,GPIO_Pin_12); //CS线拉高,结束传输
}