串口通信-学习复盘
通信协议:通信数据以帧形式传递 [波特率、起始位、数据位、奇偶校验位、停止位]以stm32f1系列芯片为例:共有5个串口,1-3是通用同步/异步串行接口USART(USART1-PA9、PA10 USART2-PA2、PA3 USART3-PB10、PB11) 4-5是通用异步串行接口UART(UART4-PC10、PC11 UART5-PC12、PD2)
代码实现:
(1)配置RX和TX引脚和串口初始化//先使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
//对io口进行配置
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//优先级配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//串口参数初始化
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1 (2串口中断函数,一般以0x0D(ascill:13表示回车\r),0x0a(ascill:
10表示换行\n)结尾,是一种自定义数据帧结束标志,同时帧头也是定义的一种开始标志,因为不知道串口什么时候发送数据,定义帧头,当接收到帧头,剩下的数据是我们需要的,以openmv为例.当接收到0x2C,0x012,在单片机中定义一个USART_Rx_Buf[]存储c(x)和c(y)的数据。 data=ustruct.pack("kkkkkk",
0x2C,
0x12,
int(cx),
int(cy),
0x0d,
0x0a) 串口中断服务函数:void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
第一次进入串口中断服务函数时,先判断是哪个串口接收到数据产生的中断,如果是,将接收到的1byte数据存储到变量里,接着判断是否还有数据没有接收完,如果接收到0x0d,0x0a,表示接收完成。USART_RX_STA有十六位,bit0-13表示有效数据,bit14表示接收到0x0d,bit15表示接收0x0a,接收完成。USART_GetFalgStatu
s函数用来检测串口中断标志位的状态,返回值为SET或者RESET。USART_GetITStatus函数用来判断中断标志位或是否发生中断,返回值为SET或者RESET。USART_ClearITPendingBit函数用在中断处理函数中对标志位进行清除操作。 (2)数据收发//发送一个字节
void usart_SendingByte(USART_TypeDef *p_USARTx,unsigned char ch)
{
while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)!=SET);
USART_SendData(p_USARTx,ch);
}
//USART_FLAG_TXE当发送数据寄存器中的数据已经取完,该标志置1,从而引发中断。 //发送字符串
void usart_sendingString(USART_TypeDef *p_USARTx,unsigned char *str)
{
unsigned int k;
do
{
usart_sending_byte(p_USARTx,*(str+k));
k++;
}
while(*(str+k)!='\0');
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET)
{}
}
/*USART_FLAG_TC当发送移位寄存器的一个字节数据已经通过TX脚一位一位移出去后,
该引脚被置1,从而引发事件中断*/ //接收一个字节数据
unsigned char usart_receive_byte(USART_TypeDef *p_USARTx,unsigned char ch)
{
while(USART_GetFlagStatus(USARTx,USART_FLAG_RXNE)!=SET);
USART_ClearITPendingBit(p_USARTx,USART_IT_RXNE);
ch=USART_ReceiveData(USARTx);
return ch;
} //测试
#define max_Buff_len 8
unsigned char USART_Buffer;
unsigned char USART_Rx=0;
void USART_Send_Receive()
{
if(USART_GetITStatus(USART1,USART_FLAG_RXNE)!=RESET)
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
Uart1_Buffer=USART_ReceiveData(USART1);
Uart1_Rx++;
if(Uart1_Buffer==0x0a||Uart1_Rx==Max_BUFF_Len)
{
if(Uart1_Buffer==0x01)
{
printf("%s\r\n",Uart1_Buffer);
Uart1_Rx=0;
}
else
{
Uart1_Rx=0;
}
}
}
} 值得学习 谢谢分享 通信协议:通信数据以帧形式传递 [波特率、起始位、数据位、奇偶校验位、停止位],非常好。 为什么分开发? 谢谢分享
USART1可以有多个时钟源
通过访问寄存器来控制I2C1工作时钟的开启。
复盘这个词用的好
硬件设计和软件设计本来就是鱼和熊掌的关系,两者不可兼得
事实上很多做项目的工程师本身自己也会在用
页:
[1]