设备间 通讯实现 可分为串行通信 和并行通讯两种。
常用的串行总线协议可分为:
1-wire 单线 一条数据线 单工 异步
uart 双线 一条数据输入线,一条数据输出线 全双工 异步
i2c 双线 一条时钟线,一条数据线 半双工 同步
spi 三线 一条时钟线,一条数据输入线,一条数据输出线 全双工 同步
串口 UART 协议
uart 串口波形
波形实现方法:
GPIO管脚模拟波形:
#define GPA1CON 0x11400020
#define ULCON2 0x13820000
#define UCON2 0x13820004
#define UBRDIV2 0x13820028
#define UFRACVAL2 0x1382002c
#define UTXH2 0x13820020
#define UTRSTAT2 0x13820010
#define rGPA1CON (*(volatile unsigned int*)GPA1CON)
#define rULCON2 (*(volatile unsigned int*)ULCON2)
#define rUCON2 (*(volatile unsigned int*)UCON2)
#define rUBRDIV2 (*(volatile unsigned int*)UBRDIV2)
#define rUFRACVAL2 (*(volatile unsigned int*)UFRACVAL2)
#define rUTXH2 (*(volatile unsigned int*)UTXH2)
#define rUTRSTAT2 (*(volatile unsigned int*)UTRSTAT2)
void uart_putc(char c);
int main(int argc, const char *argv[])
{
/* 设置GPA1控制器为UART模式 */
rGPA1CON &= ~(0xff<<0); //把寄存器的bit0~7全部清零
rGPA1CON |= 0x22<<0; //Rx,Tx
/* 设置串口协议 */
rULCON2 = 0x03; //0校验位 ,8数据位,1停止位
rUCON2 = 0x05; //轮询模式
/*
* 设置波特率:
*UART时钟信号源的值为:
*100Mhz= 100 000khz = 100 000 000hz
*本实验波特率值位115200,DIV_VAL = 100000000/(115200*16) -1 = 54.25 -1 = 53.25
*UBRDIVn = 53
*UFRACVALn/16 = 0.25 ----> UFRACVALn = 4
*/
rUBRDIV2 = 53;
rUFRACVAL2 = 4;
/* 发送状态判断 */
while(1)
{
uart_putc('c');
delay1s();
}
return 0;
}
void uart_putc(char c)
{
while(!(rUTRSTAT2&0x02));
rUTXH2 = c;
return;
}
void uart_tx(void) //模拟发送
{
rGPA1CON &= ~(0xf<<0); //把寄存器的bit0~3全部清零
rGPA1CON |= (0x1<<0); //置为Output模式
/* 发送0x04 0000 0100 0010 0000*/
rGPA1DAT &= ~(0x1<<0); //0
delay10ms();
rGPA1DAT &= ~(0x1<<0); //0
delay10ms();
rGPA1DAT |= (0x1<<0) //1
delay10ms();
rGPA1DAT &= ~(0x1<<0); //0
delay10ms();
rGPA1DAT &= ~(0x1<<0); //0
delay10ms();
rGPA1DAT &= ~(0x1<<0); //0
delay10ms();
rGPA1DAT &= ~(0x1<<0); //0
delay10ms();
rGPA1DAT &= ~(0x1<<0); //0
delay10ms();
}
void uart_rx(void) //模拟接收
{
unsigned char output=0;
rGPA1CON &= ~(0xf<<4); //把寄存器的bit0~3全部清零
rGPA1CON |= (0x0<<4); //置为Iutput模式
while(1)
{
if(GPA1DAT&0x02==0)
break;
delay10ms();
}
if(GPADAT&0x02==0)
output &= ~(1<<0);
else
output |= (1<<0);
delay10ms();
if(GPADAT&0x02==0)
output &= ~(1<<1);
else
output |= (1<<1);
delay10ms();
if(GPADAT&0x02==0)
output &= ~(1<<2);
else
output |= (1<<2);
delay10ms();
if(GPADAT&0x02==0)
output &= ~(1<<3);
else
output |= (1<<3);
delay10ms();
if(GPADAT&0x02==0)
output &= ~(1<<4);
else
output |= (1<<4);
delay10ms();
if(GPADAT&0x02==0)
output &= ~(1<<5);
else
output |= (1<<5);
delay10ms();
if(GPADAT&0x02==0)
output &= ~(1<<6);
else
output |= (1<<6);
delay10ms();
if(GPADAT&0x02==0)
output &= ~(1<<7);
else
output |= (1<<7);
}
void delay10ms(voidvoid) //延迟x ms 根据波特率计算出具体数值,这里不做具体讨论
{
for(i=0;i<100000<i++);
}
1.I2C总线是PHLIPS公司推出的一种串行总线,它只有两根双向信号线。一根是数据线SDA(serial data I/O),另一根是时钟线SCL(serial clock)。
2.如下图所示,IIC一条总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据并及时响应。
数据位的有效性规定
SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA状态才允许变化。如图所示
I2C的起始和终止信号
SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;
SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号;
响应信号( ACK ):接收器在接收到8位数据后,在第9个时钟周期,拉低SDA电平。即接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。
CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传 递信号的判断。若未收到应答信号,由此可判断为受控单元出现故障;如图所示:
下列三种情况不会有ACK信号:
A、当从机不能响应从机地址时(从机忙于其他事无法响应IIC总线操作或这个地址没有对应从机),在第9个SCL周期内SDA线没有被拉低,即没有ACK信号。这时,主机发送一个P信号终止传输或者重新发送一个S信号开始新的传输
B、从机接收器在传输过程中不能接收更多的数据时,也不会发出ACK信号。主机意识到这点,从而发出一个P信号终止传输或者从新发送一个S信号开始新的传输
C、主机接收器在接收到最后一个字节时,也不会发出ACK信号,于是,从机发送器释放SDA线,允许主机发送P信号结束传输
I2C字节的传送与应答
每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位).如图所示
应答位的作用
主机在发送数据时,每次发送一字节数据,都需要读取从机应答位,当从机空闲可以接收该字节数据时,从机会发出应答(一帧数据的第9位为“0”),当从机正忙于其他工作的处理来不及接收主机发送的数据时,从机会发出非应答(一帧数据的第9位为“1”)主机则应发出终止信号以结束数据的继续传送,主机通过从机发出的应答位来判断从机是否成功接收数据.
当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。
贴个IIC总线在传送数据过程中信号时序图.好好研究好时序图,一切都可以轻松解决.
I2C是如何通信的
s3C4412发送或接受数据是如何区分多个子设备?可以看下图:开始通信以后,主设备首先会发送7bit位的slave device地址,和1bit位的read或者write命令,
(1)如果为write命令,则主设备free SDA通信线(If the I2C-bus is free, both SDA and SCL lines should be both at High level.如果I2C-bus是空闲的,那么SDA和SCL线都应该处于高电平。),即SDA为高位。然后从设备先ACK主设备(拉低SDA)表示收到命令(S)。然后主设备在发送8bit数据,从设备在ACK(A)。然然后结束(P)。
(2)如果为read命令,则从设备先ACK主设备(拉低SDA),然后发送8bit数据,主设备ACK从设备(拉低SDA),从设备在发送,直到主设备停止接收。
如下图所以,白色bit位为主设备发送,灰色bit位为从设备发送。
栗子
工作原理:在系统运行以后也就启动了看门狗的计数器,看门狗就开始自动计数,如果到了一定的时间还不去清看门狗,那么看门狗计数器就会溢出从而引起看门狗中断,造成系统复位
PWM(Pulse Width Modulation) : 脉冲宽度调制 。常见应用有:电机控制,DAC输出等
Exynos 4412 SCP有5个32位脉宽调制(PWM)定时器。这些定时器为ARM子系统产生内部中断。此外,定时器0、1、2和3包括一个驱动外部I/O信号的PWM功能。
设置一级分频 TCFG0
设置二级分频 TCFG1
设置三级分频 TCNTB0
TCMPB0 占空比
占空比是指高电平在一个周期之内所占的时间比率
TCNTBn是脉冲总宽度,TCMPBn是高电平宽度,占空比 = TCNTBn/TCNTBn
TCON
//PWM timer //#define GPX2CON 0x11000C40 //#define rGPX2CON (*(volatile unsigned int*)GPX2CON) #define TCMPB0 0x139D0010 #define TCON 0x139D0008 #define rTCMPB0 (*(volatile unsigned int*)TCMPB0) #define rTCON (*(volatile unsigned int*)TCON) void do_pwm(void) { rTCMPB0 = 100; //占空比 = 100/200 = 0.5 rTCON &= ~(0xF); //先用TCON 更新定时器timer0的 TCMPB0和TCMPB0值 rTCON |= 0xA; rTCON &= ~(0xF); //再用TCON开启 定时器timer 0 rTCON |= 0x9; }
//uart #define GPA1CON 0x11400020 #define ULCON2 0x13820000 #define UCON2 0x13820004 #define UBRDIV2 0x13820028 #define UFRACVAL2 0x1382002c #define UTXH2 0x13820020 #define UTRSTAT2 0x13820010 #define rGPA1CON (*(volatile unsigned int*)GPA1CON) #define rULCON2 (*(volatile unsigned int*)ULCON2) #define rUCON2 (*(volatile unsigned int*)UCON2) #define rUBRDIV2 (*(volatile unsigned int*)UBRDIV2) #define rUFBRACVAL2 (*(volatile unsigned int*)UFRACVAL2) #define rUTXH2 (*(volatile unsigned int*)UTXH2) #define rUTRSTAT2 (*(volatile unsigned int*)UTRSTAT2) //interrupt #define GPX1CON 0x11000c20 #define EXT_INT41CON 0x11000E04 #define EXT_INT41_MASK 0x11000F04 #define ICDISER1_CPU0 0x10490104 #define ICDDCR 0x10490000 #define ICCPMR_CPU0 0x10480004 #define ICCICR_CPU0 0x10480000 #define ICCIAR_CPU0 0x1048000C #define EXT_INT41_PEND 0X11000F44 #define ICDICPR1_CPU0 0x10490284 #define ICCEOIR_CPU0 0x10480010 #define ICDIPTR14_CPU0 0x10490838 #define rGPX1CON (*(volatile unsigned int*)GPX1CON) #define rEXT_INT41CON (*(volatile unsigned int*)EXT_INT41CON) #define rEXT_INT41_MASK (*(volatile unsigned int*)EXT_INT41_MASK) #define rICDISER1_CPU0 (*(volatile unsigned int*)ICDISER1_CPU0) #define rICDDCR (*(volatile unsigned int*)ICDDCR) #define rICCPMR_CPU0 (*(volatile unsigned int*)ICCPMR_CPU0) #define rICCICR_CPU0 (*(volatile unsigned int*)ICCICR_CPU0) #define rICCIAR_CPU0 (*(volatile unsigned int*)ICCIAR_CPU0) #define rEXT_INT41_PEND (*(volatile unsigned int*)EXT_INT41_PEND) #define rICDICPR1_CPU0 (*(volatile unsigned int*)ICDICPR1_CPU0) #define rICCEOIR_CPU0 (*(volatile unsigned int*)ICCEOIR_CPU0) #define rICDIPTR14_CPU0 (*(volatile unsigned int*)ICDIPTR14_CPU0) //PWM #define GPD0CON 0x114000A0 #define TCFG0 0x139D0000 #define TCFG1 0x139D0004 #define TCNTB0 0x139D000C #define TCMPB0 0x139D0010 #define TCON 0x139D0008 #define rGPD0CON (*(volatile unsigned int*)GPD0CON) #define rTCFG0 (*(volatile unsigned int*)TCFG0) #define rTCFG1 (*(volatile unsigned int*)TCFG1) #define rTCNTB0 (*(volatile unsigned int*)TCNTB0) #define rTCMPB0 (*(volatile unsigned int*)TCMPB0) #define rTCON (*(volatile unsigned int*)TCON) void uart_putc(char c); void do_irq(void); void do_pwm(void); void do_key3(void); void do_int(void); int main(int argc, const char *argv[]) { //key3 do_key3(); //pwm do_pwm(); //int do_int(); return 0; } void uart_putc(char c) { while(!(rUTRSTAT2&0X02)); rUTXH2 = c; return; } void do_key3(void) { //KEY3 /* 设置GPA1控制器为UART模式 */ rGPA1CON &= ~(0xff<<0); //把寄存器的bit0~7全部清零 rGPA1CON |= 0X22<<0; //Rx,Tx /* 设置串口协议 */ rULCON2 = 0x03; //0校验位 ,8数据位,1停止位 rUCON2 = 0x05; //轮询模式 /* * 设置波特率: *UART时钟信号源的值为: *100Mhz= 100 000khz = 100 000 000hz *本实验波特率值位115200,DIV_VAL = 100000000/(115200*16) -1 = 54.25 -1 = 53.25 *UBRDIVn = 53 *UFRACVALn/16 = 0.25 ----> UFRACVALn = 4 */ rUBRDIV2 = 53; rUFBRACVAL2 = 4; } void do_int(void) { //INT /* 外设层次上,配置管脚工作模式 */ rGPX1CON &= ~(0xf<<4); //设置为中断模式 rGPX1CON |= (0xF<<4); rEXT_INT41CON &= ~(0X7<<4); rEXT_INT41CON |= (0X02<<4); //设置为下降沿触发 rEXT_INT41_MASK &= ~(1<<1); //外设引脚使能中断,可以让中断发生 /* GIC(通用中断控制器)层次上 */ rICDISER1_CPU0 = rICDISER1_CPU0 | (1<<25); //使能当前中断 rICDDCR |= (1<<0); //全局使能GIC 让所有中断可以通过GIC到达CPU rICCPMR_CPU0 |= (0xFF<<0); //设置中断优先级掩码级别为255 /* CPU内核层次上 */ rICCICR_CPU0 |= (1<<0); //CPU0的全局使能中断,所有中断都可以通过CPU接口到达内核 rICDIPTR14_CPU0 = 0x01010101; //照抄,意义不明 /* 发送状态判断 */ while(1) { uart_putc('c'); delay1s(); } } void do_irq(void) //参数中的void表示该函数不允许接受参数,理论上不写也可以,为了方便编译器通过,处于严谨考虑,还是要写上 { int irq_num; irq_num = rICCIAR_CPU0&0x3FF; //获取中断号 switch(irq_num) { case 57: uart_putc('k'); uart_putc('2'); rEXT_INT41_PEND |= 1<<1; //清GPX1_1中断位 rICDICPR1_CPU0 |= 1<<25; //清GIC GPX1_1中断位 break; default: uart_putc('x'); break; } rICCEOIR_CPU0 &= (~0x3FF); rICCEOIR_CPU0 |= irq_num; //结束中断 将处理完成的中断ID号写入该寄存器 表示相应的中断处理完成 } void do_pwm(void) { rGPD0CON &= ~(0xF); //GPD0_0设置为PWM模式 rGPD0CON |= 0x02; rTCFG0 &= ~(0xFF); //TCFG0[7:0] 设置1分频为100 rTCFG0 |= 0x64; rTCFG1 &= ~(0xF); //TCFG1[3:0]设置2分频为8 rTCFG1 |= 0x3; rTCNTB0 = 200; //TCNTB0 设置3分频为200 rTCMPB0 = 100; //占空比 = 100/200 = 0.5 rTCON &= ~(0xF); //先用TCON 更新定时器timer0的 TCMPB0和TCMPB0值 rTCON |= 0xA; rTCON &= ~(0xF); //再用TCON开启 定时器timer 0 rTCON |= 0x9; }