这次来完成这两个题目
UART,即通用异步接收器/发送器,是嵌入式开发中最常用的设备间通信协议之一,在开发中经常会用它来打印调试信息,可以最少使用2个引脚(TX和RX)实现数据传输(设备间需要共地),如果是单向传输可以只用一个引脚,开发板上已经实现了USB转UART的电路,使用引脚是PA9和PA10
使用串口涉及到GPIO复用功能,复用表可以在数据手册中找到
初始化串口,波特率设置为115200,停止位1,无校验,开启接收中断和空闲中断
void uart_init(void)
{
GPIO_InitTypeDef gpio_cfg;
NVIC_InitTypeDef nvic_cfg;
UART_InitTypeDef uart_cfg;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
UART_StructInit(&uart_cfg);
uart_cfg.BaudRate = 115200;
uart_cfg.WordLength = UART_WordLength_8b;
uart_cfg.StopBits = UART_StopBits_1;
uart_cfg.Parity = UART_Parity_No;
uart_cfg.HWFlowControl = UART_HWFlowControl_None;
uart_cfg.Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(UART1, &uart_cfg);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_7);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_7);
GPIO_StructInit(&gpio_cfg);
gpio_cfg.GPIO_Pin = GPIO_Pin_9;
gpio_cfg.GPIO_Speed = GPIO_Speed_High;
gpio_cfg.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &gpio_cfg);
gpio_cfg.GPIO_Pin = GPIO_Pin_10;
gpio_cfg.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &gpio_cfg);
nvic_cfg.NVIC_IRQChannel = UART1_IRQn;
nvic_cfg.NVIC_IRQChannelPreemptionPriority = 0;
nvic_cfg.NVIC_IRQChannelSubPriority = 1;
nvic_cfg.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_cfg);
UART_Cmd(UART1, ENABLE);
UART_ITConfig(UART1, UART_IT_RX|UART_IT_RXIDLE, ENABLE);
}
中断函数处理,先实现将收到的数据原样返回的功能,创建接收缓冲区,通过接收中断向缓冲区写入数据,当触发空闲中断或缓冲区写入超过一半后,开始发送数据
#define UART_BUFFER_LEN 50
uint8_t uart_buffer[UART_BUFFER_LEN] = {0};
uint8_t uart_rxindex = 0;
uint8_t uart_rxlen = 0;
uint8_t uart_txindex = 0;
uint8_t uart_txlen = 0;
void readrxtotxbuffer()
{
uart_txlen += uart_rxlen;
uart_rxlen = 0;
UART_ITConfig(UART1, UART_IT_TX, ENABLE);
}
void UART1_IRQHandler(void)
{
if(UART_GetITStatus(UART1, UART_IT_RX) == SET){
UART_ClearITPendingBit(UART1, UART_IT_RX);
uart_buffer[uart_rxindex++] = UART_ReceiveData(UART1);
if(uart_rxindex == UART_BUFFER_LEN)
uart_rxindex = 0;
uart_rxlen++;
}
if(UART_GetITStatus(UART1, UART_IT_RXIDLE) == SET|| uart_rxlen == UART_BUFFER_LEN/2) {
UART_ClearITPendingBit(UART1, UART_IT_RXIDLE);
readrxtotxbuffer();
}
if(UART_GetITStatus(UART1, UART_IT_TX) == SET){
UART_ClearITPendingBit(UART1, UART_IT_TX);
if(uart_txlen > 0)
{
UART_SendData(UART1,uart_buffer[uart_txindex++]);
if(uart_txindex == UART_BUFFER_LEN)
uart_txindex = 0;
uart_txlen--;
}
else
{
UART_ITConfig(UART1, UART_IT_TX, DISABLE);
}
}
}
运行效果
接下来实现将printf输出重定向到串口,方便打印调试信息,如果使用Keil开发的话可以很简单就实现,先勾选这个
然后添加下面这段代码
int fputc(int ch, FILE *f)
{
UART_SendData(UART1, ch);
while (UART_GetFlagStatus(UART1, UART_FLAG_TXC) == RESET);
return ch;
}
在main函数中测试printf输出
int main(void)
{
led_init();
keys_init();
exit_init();
uart_init();
printf("This is printf test! %d",666);
while (1)
{
}
}
运行效果
在开发中经常会有让某个特定任务按周期运行的需要,或者对输入信号进行计数,这时候就要用到定时器了,除此之外PWM也要用到定时器,接下来实现一个1s的定时器,MM32F5330拥有的定时器资源如下
接下来使用基础定时器6实现这个1秒的定时器,先看一下系统框图,Timer6在APB1上
系统初始化时默认180MHz的时钟频率,APB1不分频,这样将Timer6的预分频系数设置为18000-1,重载值设置为10000-1就得到了一个1s触发的定时器,代码如下
void timer6_init(void)
{
NVIC_InitTypeDef nvic_cfg;
TIM_TimeBaseInitTypeDef timer_cfg;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_TimeBaseStructInit(&timer_cfg);
timer_cfg.TIM_Prescaler = (TIM_GetTIMxClock(TIM6) / 10000 - 1);
timer_cfg.TIM_CounterMode = TIM_CounterMode_Up;
timer_cfg.TIM_Period = (10000 - 1);
timer_cfg.TIM_ClockDivision = TIM_CKD_Div1;
timer_cfg.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM6, &timer_cfg);
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
nvic_cfg.NVIC_IRQChannel = TIM6_IRQn;
nvic_cfg.NVIC_IRQChannelPreemptionPriority = 0;
nvic_cfg.NVIC_IRQChannelSubPriority = 1;
nvic_cfg.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_cfg);
TIM_Cmd(TIM6, ENABLE);
}
在中断中累加计数并发送
uint8_t sec_flag = 0;
uint32_t test_count = 0;
void TIM6_IRQHandler(void)
{
if (RESET != TIM_GetITStatus(TIM6, TIM_IT_Update))
{
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
test_count += 1;
printf("sec count %d",test_count);
}
}
运行效果
|