【沁恒CH582】 I2C暂存的继电器上位机控制继电器模块
[复制链接]
i2c接口与继电器驱动
本次我们将要实现的功能是上位机发送继电器控制指令(单字节控制指令),24C256EEPROM用于保存当前的继电器状态,并且在保存之后驱动继电器处于正确的状态。
接下来我们逐步完成。首先我们需要选定一个基础例程,在此基础上进行改动,我计划选用IIC例程进行开发。
对于IIC例程做基础分析加工
首先测试是否能正确烧写例程。链接硬件如下:
就是为开发板连上编程器和串口转换器,然后链接一个24C256的EEPROM。(目前手上最方便的就是这块测试板了)。
然后编译例程,并烧录。
接下来的问题就是确定数据保存的位置和代码中对于数据定位的部分调整了。
接下来我们先做一个简单的测试,我们使用单字节写入的模式对芯片的第一个地址空间和最后一个地址空间分别写入0x56和0x78,然后读出这两个地址空间的数据并发送到上位机中。
首先是器件地址,根据数据手册中给出的描述10100A_1A_0\,b我们将A_1,A_0连接到低电平,然后我们可以得到器件地址为1010000\,B=0x50。
接下来将需要向器件中输入16bits的地址,之后紧跟8bits的具体数据,最终发送停止位。
按照我们的定义,我们将要发送[BEGIN] [DEVICE ADDR] 0x00 0x00 0x56 [STOP] ,[BEGIN] [DEVICE ADDR] 0xFF 0xFF 0x78 [STOP] ,我们知道256k的EEPROM的首地址应当为0x0000 末地址为0xFFFF 。那么按照这个逻辑可以得到对应的代码。
void send_one_byte(uint8_t data)
{
I2C_SendData(data);
while(I2C_GetFlagStatus(I2C_FLAG_TXE) != RESET);
}
void main()
{
// ... 初始化部分
I2C_GenerateSTART(ENABLE);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(0x50, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
send_one_byte(0x00);
send_one_byte(0x00);
send_one_byte(0x56);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(ENABLE);
I2C_GenerateSTART(ENABLE);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(0x50, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
send_one_byte(0xff);
send_one_byte(0xff);
send_one_byte(0x78);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(ENABLE);
// ... 后续循环部分
}
接下来我们应该读取这两个地址的内容并发送到上位机。
根据数据手册上的说明这款芯片的接收时序是这样的:
那么我们可以啰嗦地,写出逐个字节读取地代码。
I2C_GenerateSTART(ENABLE);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(0x50, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
send_one_byte(0xff);
send_one_byte(0xff);
send_one_byte(0x78);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(ENABLE);
printf("Data has been sent!\r\n");
I2C_GenerateSTART(ENABLE);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(0x50, I2C_Direction_Receiver);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
send_one_byte(0x00);
send_one_byte(0x00);
send_one_byte(0xff);
send_one_byte(0xff);
I2C_GenerateSTART(ENABLE); // 重复产生起始位
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED));
printf("Addr:0x0000, Data: 0x%x",I2C_ReceiveData());
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED));
printf("Addr:0xFFFF, Data: 0x%x",I2C_ReceiveData());
有了这一段代码可以验证是否数据被正确存入指定区域。
UART接收中断
首先是初始化代码:
GPIOA_SetBits(GPIO_Pin_9);
GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
UART1_DefInit();
UART1_ByteTrigCfg( UART_1BYTE_TRIG );
trigB = 1;
UART1_INTCfg( ENABLE, RB_IER_RECV_RDY|RB_IER_LINE_STAT );
PFIC_EnableIRQ( UART1_IRQn );
之后是中断响应函数:
__INTERRUPT
__HIGH_CODE
void UART1_IRQHandler( void )
{
UINT8V i;
switch ( UART1_GetITFlag() )
{
case UART_II_LINE_STAT : // 线路状态错误
{
UART1_GetLinSTA();
break;
}
case UART_II_RECV_RDY : // 数据达到设置触发点
for ( i = 0; i != trigB; i++ )
{
RxBuff[i] = UART1_RecvByte();
UART1_SendByte( RxBuff[i] );
}
break;
case UART_II_RECV_TOUT : // 接收超时,暂时一帧数据接收完成
i = UART1_RecvString( RxBuff );
UART1_SendString( RxBuff, i );
break;
case UART_II_THR_EMPTY : // 发送缓存区空,可继续发送
break;
case UART_II_MODEM_CHG : // 只支持串口0
break;
default :
break;
}
}
这样我们就得到了一个ECHO例程。
继电器模块
我们假设输入A-D表示吸合1-4继电器,那么就是在GPIOA1-GPIO4执行输出。E-H表示关闭GPIOA1-GPIOA4。我们可以魔改一下上面的程序。
将GPIO 设为低电流推挽输出,GPIO_ModeOut_PP_5mA 。
__INTERRUPT
__HIGH_CODE
void UART1_IRQHandler( void )
{
UINT8V i;
switch ( UART1_GetITFlag() )
{
case UART_II_LINE_STAT : // 线路状态错误
{
UART1_GetLinSTA();
break;
}
case UART_II_RECV_RDY : // 数据达到设置触发点
for ( i = 0; i != trigB; i++ )
{
RxBuff[i] = UART1_RecvByte();
UART1_SendByte( RxBuff[i] );
switch(RxBuff[i])
{
case 'A':
GPIOA_SetBits(GPIO_Pin_1);
break;
case 'B':
GPIOA_SetBits(GPIO_Pin_2);
break;
case 'C':
GPIOA_SetBits(GPIO_Pin_3);
break;
case 'D':
GPIOA_SetBits(GPIO_Pin_4);
break;
case 'E':
GPIOA_ResetBits(GPIO_Pin_1);
break;
case 'F':
GPIOA_ResetBits(GPIO_Pin_2);
break;
case 'G':
GPIOA_ResetBits(GPIO_Pin_3);
break;
case 'H':
GPIOA_ResetBits(GPIO_Pin_4);
break;
default:
break;
}
//UART1_SendByte(RxBuff[i]);
}
break;
case UART_II_RECV_TOUT : // 接收超时,暂时一帧数据接收完成
i = UART1_RecvString( RxBuff );
UART1_SendString( RxBuff, i );
switch(RxBuff[i])
{
case 'A':
GPIOA_SetBits(GPIO_Pin_1);
break;
case 'B':
GPIOA_SetBits(GPIO_Pin_2);
break;
case 'C':
GPIOA_SetBits(GPIO_Pin_3);
break;
case 'D':
GPIOA_SetBits(GPIO_Pin_4);
break;
case 'E':
GPIOA_ResetBits(GPIO_Pin_1);
break;
case 'F':
GPIOA_ResetBits(GPIO_Pin_2);
break;
case 'G':
GPIOA_ResetBits(GPIO_Pin_3);
break;
case 'H':
GPIOA_ResetBits(GPIO_Pin_4);
break;
default:
break;
}
break;
case UART_II_THR_EMPTY : // 发送缓存区空,可继续发送
break;
case UART_II_MODEM_CHG : // 只支持串口0
break;
default :
break;
}
}
利用万用表可以测量得到这种情况下,低电平为0V,高电平为5.12V.
缝合!
将以上代码,缝合到一起,让我们每次的继电器操作结果能够保存在EEPROM中。可以用下面的代码实现:
首先是初始化继电器初始状态:
uint8_t state;
I2C_GenerateSTART(ENABLE);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(0x50, I2C_Direction_Receiver);
while (!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
send_one_byte(0x00);
send_one_byte(0x00);
I2C_GenerateSTART(ENABLE); // 重复产生起始位
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED));
state = I2C_ReceiveData();
printf("Addr:0x0000, Data: 0x%x",state); // 初始化
接下来是更新中断响应函数:
__INTERRUPT
__HIGH_CODE
void UART1_IRQHandler( void )
{
UINT8V i;
switch ( UART1_GetITFlag() )
{
case UART_II_LINE_STAT : // 线路状态错误
{
UART1_GetLinSTA();
break;
}
case UART_II_RECV_RDY : // 数据达到设置触发点
for ( i = 0; i != trigB; i++ )
{
RxBuff[i] = UART1_RecvByte();
UART1_SendByte( RxBuff[i] );
switch(RxBuff[i])
{
case 'A':case 'B':case 'C':case 'D':
state |= 1<<(RxBuff[i] - 'A');
break;
case 'E':case 'F':case 'G':case 'H':
state &= 0xFF^1<<(RxBuff[i] - 'A');
break;
default:
break;
}
}
break;
case UART_II_RECV_TOUT : // 接收超时,暂时一帧数据接收完成
i = UART1_RecvString( RxBuff );
UART1_SendString( RxBuff, i );
switch(RxBuff[i])
{
case 'A':case 'B':case 'C':case 'D':
state |= 1<<(RxBuff[i] - 'A');
break;
case 'E':case 'F':case 'G':case 'H':
state &= 0xFF^1<<(RxBuff[i] - 'A');
break;
default:
break;
}
}
break;
case UART_II_THR_EMPTY : // 发送缓存区空,可继续发送
break;
case UART_II_MODEM_CHG : // 只支持串口0
break;
default :
break;
}
R32_PA_OUT = state << 1; // 从A1开始
}
至此我们就实现了一个可以由上位机控制的继电器组了!虽然我们只是用万用表测量了一下输出情况,但是已经能够起到我们预想中的效果了。
|