一、STM32F1串口资源(5个串口-3个同步,2个异步)
串口号 | 发送(TX) | 接收(RX) |
USART1 | PA9 | PA10 |
USART2 | PA2 | PA3 |
USART3 | PB10 | PB11 |
UART4 | PC10 | PC11 |
UART5 | PC12 | PD2 |
二、异步串口通信数据构成:
起始位-0+数据位(8bit)+[奇偶校验位(第9bit)]+停止位-1(1/1.5/2bit)
什么是奇偶校验:
奇校验:8个数据位,加起来如果是偶数个1,则第9位自动补1;加起来如果是奇数个1,则第9位就自动补0;目的就是最终:使数据有奇数个1。
偶校验:同理。目的是最终:使数据有偶数个1。
三、框图分析(不太会分析,先看个大概吧)
四、波特率的计算:
USARTDIV=整数部分+小数部分
整数部分=DIV_Mantissa;小数部分=(DIV_Fraction/16)
以串口1为例:USART1的时钟,使用的是APB2总线上的PCLK2(默认72M)
要将波特率设置为115200,则:Baud=115200
三个未知数,还剩下USARTDIV=Fpclk/(Baud*16)=72M/(115200*16)≈39.0625
USARTDIV = 39+0.0625
整数部分39,直接写入:DIV_Mantissa=39=0x27
小数部分0.0625,0.0625=(DIV_Fraction/16):DIV_Fraction=1=0x01
通过查阅寄存器,小数部分占了4bit,整数部分占12bit;因此,要设置波特率为115200,需要向寄存器写入:USART1_BRR=0x0271
五、库函数应用
串口配置步骤:
1、串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
2、串口复位:USART_DeInit();
3、GPIO端口模式设置:GPIO_Init();
发送(PA9):推挽输出
接收(PA10):上拉输入 或 浮空输入
4、串口参数初始化:USART_Init();
5、开中断,并初始化NVIC:NVIC_Init(); & USART_ITConfig();
6、使能串口:USART_Cmd();
7、编写中断处理函数:USARTx——IRQHandler();
8、串口数据收发:USART_SendData(); & USART_RceviceData();
9、串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
--------------------------------------------------------------------------------------------------------------------------
根据时钟树可以知道:USART1是挂载在APB2下,因此:从stm32f10x_rcc.h找到:
RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
//如果不知道填哪些参数,可以F12进入函数的定义,进入到参数有效性检查中查看可填参数列表
void Usart1_Config(void)
{
USART_InitTypeDef USART1_nitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
USART_DeInit(USART1);
Usart1_GPIO_Config();
Usart1_NVIC_Config();
USART1_nitStructure.USART_BaudRate=115200;
USART1_nitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART1_nitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART1_nitStructure.USART_Parity=USART_Parity_No;
USART1_nitStructure.USART_StopBits=USART_StopBits_1;
USART1_nitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART1_nitStructure);
USART_Cmd(USART1,ENABLE);
}
void Usart1_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
如果使用中断,则还需要配置优先级:misc.c中:
NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)//优先级分组//为了避免冲突,只分组一次,一般放到main函数中。
void Usart1_NVIC_Config(void)
{
NVIC_InitTypeDef USART1_NVIC_InitStructure;
USART1_NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
USART1_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=15;
USART1_NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
USART1_NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
NVIC_Init(&USART1_NVIC_InitStructure);
}
中断处理函数(函数名一定要写对!!!)
/**
* @brief This function handles Usart1 interrupt request.
* @param None
* @retval None
*/
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
USART_SendData(USART1,USART_ReceiveData(USART1));
}
}
/************************************中断接收优化******************************************************/
/**
* @brief This function handles Usart1 interrupt request.
* @param None
* @retval None
*/
void USART1_IRQHandler(void)
{
u8 temp_Rx_Data = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) == 1 ) //判断接收到的中断,是否为接收中断
{
temp_Rx_Data=USART_ReceiveData(USART1);//读出RD寄存器数据
if((USART1_RX_State & (1<<15)) == 0) //判断是否接收未接收完成(0A)
{
if((USART1_RX_State & (1<<14)) == (1<<14))//判断上一个数据是否为0x0D
{
if(temp_Rx_Data != 0x0A)//接收数据不是0D 0A
{
USART1_RX_State=0;//清空标记和计数重新接收
}
else
{
USART1_RX_State |= (1<<15);//接收完成标记
}
}
else//未收到0x0D
{
if(temp_Rx_Data == 0x0D)
{
USART1_RX_State |= (1<<14);//标记接收到0x0D
}
else//存数据
{
USART1_Rx_Data[USART1_RX_State & 0x3fff]=temp_Rx_Data;//保存数据(只用了0--13bit)
//由于接收数据时候,不会遇到标志位,置位标志位之后也不应该有数据,所以可以清0
USART1_RX_State++;//计数+1
if(USART1_RX_State >= UART1_Rx_LEN-1)//0-199//数据超了,重新接收
{
USART1_RX_State=0;
}
}
}
}
}
else
{
//主函数还未处理上次数据,不接收新数据
}
}
参考原子写的,有点绕,自己慢慢分析吧,来不及分析了。我要修改前面写的用systick写delay做延时的内容去了。(写的太恶心了)
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
SysTick_Config(SystemCoreClock/1000);
LED_GPIO_Config();
Usart1_Config(115200);
while(1)
{
if(SysTickTime>=1000)
{
GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13)?
GPIO_ResetBits(GPIOC, GPIO_Pin_13),
SysTickTime=0:
GPIO_SetBits(GPIOC, GPIO_Pin_13),
SysTickTime=0;
}
if(USART1_RX_State & (1<<15))//接收到一组数据
{
len = (USART1_RX_State & 0x3fff);//从13位数据中获取数据长度
for(u16 n=0;n<len;n++)
{
USART_SendData(USART1,USART1_Rx_Data[n]);
while((USART_GetFlagStatus(USART1,USART_FLAG_TC)) == 0);//一帧数据发送结束判断TC位
}
printf("\r\n");
USART1_RX_State=0;
}
}
}
用printf函数,需要增加一个函数重映射,否则用不了
//重定义fputc函数
int fputc(int ch,FILE *f)
{
while((USART1->SR&0x40) == 0);
USART1->DR = (u8)ch;
return ch;
}
自己写的串口发送函数:
1、发送任意长度的字符串
char Data[]="12345678\r\n";
void SendData_char(char *p,u8 n)
{
printf("n=%d\r\n",n);
for(;n>0;n--)//sizeof,求字符串长度,带了'\0'
{
USART1->DR = (u8) (*p & 0xFF);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
p++;
}
}
2、发送32位十六进制数据(有待完善。。。)
void SendData_int32(u32 data)
{
int i=3;
do
{
USART1->DR = (u8) ((data & 0xFF000000) >> 24);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
data = data << 8;
}
while(i--);
}
SendData_int32(0x12345678);