先申请,本栏目用的都是GD32F470芯片240M,软件用的是keil,编写用的是C++(其实和C没有区别).
C++格式下怎么使用printf
因为keil下使用C++,要把Micro LIB去掉 ,所以printf要重新重定向
看下面的代码,是很久之前 抄某一个大神的,
#pragma import(__use_no_semihosting)
namespace std{
struct __FILE
{
int handle;
};
FILE __stdout;
FILE __stdin;
FILE __stderr;
int fputc(int ch, std::FILE *f)
{
while(RESET == usart_flag_get(USART2, USART_FLAG_TBE));
usart_data_transmit(USART2, (uint8_t)ch);
return ch;
}
int ferror(FILE *stream)
{
/* Your implementation of ferror(). */
return 0;
}
int fflush(FILE *f)
{
/* Your implementation of fflush(). */
return 0;
}
extern "C" void _sys_exit(int)
{
/* declared in <stdlib.h> */
// abort();
while(1);
}
extern "C" void _ttywrch(int ch)
{
while(RESET == usart_flag_get(USART2, USART_FLAG_TBE));
usart_data_transmit(USART2, (uint8_t)ch);
//return ch;
return ;
}
}
然后下面是usart的内容
直接上代码
new一个对象
Usart_class *Usart1_Obj = new Usart_class(USART2,115200);
Global_Usart1_Obj=Usart1_Obj;
IO配置
:我用的是串口2,然后配置成推挽,备用功能(复用功能),上拉。
具体的IO口配置看我的GPIO篇
void Usart_class::usart_io_init(void)
{
if(__usart_periph == USART2)
{
rcu_periph_clock_enable(RCU_GPIOD);
// connect port to USARTx_Tx,USARTx_Rx
gpio_af_set(GPIOD, GPIO_AF_7, GPIO_PIN_8|GPIO_PIN_9);
// configure USART Tx as alternate function push-pull */
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_8|GPIO_PIN_9);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_8|GPIO_PIN_9);
}
}
Usart配置:
在这里,开启空闲中断和使能时钟,配置dma和使能发送接收,最后开启串口
要注意的是,usart_deinit函数要放在usart_interrupt_enable函数前面,不然不工作。
void Usart_class::usart_init()
{
if(__usart_periph == USART2)
{
rcu_periph_clock_enable(RCU_USART2);
nvic_irq_enable(USART2_IRQn, 1, 1);
}
else
return;
usart_io_init();
usart_deinit(__usart_periph); //取消初始化要放在中断usart_interrupt_enable前面
usart_interrupt_enable(__usart_periph, USART_INT_IDLE);//空闲中断
usart_baudrate_set(__usart_periph,__baudval);
usart_receive_config(__usart_periph, USART_RECEIVE_ENABLE);
usart_transmit_config(__usart_periph, USART_TRANSMIT_ENABLE);
usart_dma_recv_config();
usart_enable(__usart_periph);
}
DMA配置:
发送DMA配置:
注意DMA的通道即可,发送是通道3,最前面的通道对应函数
dma_channel_subperipheral_select(DMA0, DMA_CH3, DMA_SUBPERI4);
的最后一个参数DMA_SUBPERI4。
void Usart_class::usart_dma_Send_config(void)
{
if(__usart_periph == USART2)
{
dma_single_data_parameter_struct dma_init_struct_send;
// enable DMA0
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(DMA0, DMA_CH3);
dma_init_struct_send.direction = DMA_MEMORY_TO_PERIPH;
dma_init_struct_send.memory0_addr = (uint32_t)usart_sendbuf;//类成员变量
dma_init_struct_send.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct_send.periph_memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct_send.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_init_struct_send.number = Usart_SendLen;//类成员变量
dma_init_struct_send.periph_addr = USART2_DATA_ADDRESS;
dma_init_struct_send.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct_send.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(DMA0, DMA_CH3, &dma_init_struct_send);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH3);
dma_channel_subperipheral_select(DMA0, DMA_CH3, DMA_SUBPERI4);
/* enable DMA channel_3 */
dma_channel_enable(DMA0, DMA_CH3);
usart_dma_transmit_config(__usart_periph, USART_DENT_ENABLE);
/* wait DMA Channel transfer complete */
// while(RESET == dma_flag_get(DMA0, DMA_CH3, DMA_INTF_FTFIF));
}
}
这里的内存地址是 uint8_t usart_sendbuf[USART_MAX_LEN];
USART_MAX_LEN = 256
硬件地址是
#define USART2_DATA_ADDRESS ((uint32_t)0x40004800 + 0x04)
发送的字节Usart_SendLen是不定的,需要你每次发送数据时对Usart_SendLen赋值,然后都要执行下面这一整段代码来配置dma,才能发送出去
usart_dma_transmit_config(__usart_periph, USART_DENT_ENABLE);
这句库函数代码就是使能发送
void usart_dma_transmit_config(uint32_t usart_periph, uint32_t dmacmd)
{
uint32_t ctl = 0U;
ctl = USART_CTL2(usart_periph);
ctl &= ~USART_CTL2_DENT;
ctl |= dmacmd;
/* configure DMA transmission */
USART_CTL2(usart_periph) = ctl;
}
接收DMA
注意要我代码的注释:
dma_init_struct_recv.number = USART_MAX_LEN; //使用宏,不像发送那样有几个才配置几个,是程序一开始就配置了,然后在接收中断那里接收完后再配置
这是能不断用DMA接收的原因。
//配合串口的空闲中断
void Usart_class::usart_dma_recv_config(void)
{
dma_single_data_parameter_struct dma_init_struct_recv;
if(__usart_periph == USART2)
{
// enable DMA0
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(DMA0, DMA_CH1);
dma_init_struct_recv.direction = DMA_PERIPH_TO_MEMORY;
dma_init_struct_recv.memory0_addr = (uint32_t)usart_recebuf;
dma_init_struct_recv.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct_recv.periph_memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct_recv.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_init_struct_recv.number = USART_MAX_LEN; //使用宏,不像发送那样有几个才配置几个,是程序一开始就配置了,然后在接收中断那里接收完后再配置
dma_init_struct_recv.periph_addr = USART2_DATA_ADDRESS;
dma_init_struct_recv.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct_recv.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(DMA0, DMA_CH1, &dma_init_struct_recv);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH1);
dma_channel_subperipheral_select(DMA0, DMA_CH1, DMA_SUBPERI4);
/* enable DMA channel2 */
dma_channel_enable(DMA0, DMA_CH1);
usart_dma_receive_config(__usart_periph, USART_DENR_ENABLE);
// while(RESET == dma_flag_get(DMA0, DMA_CH1, DMA_INTF_FTFIF));
}
}
中断函数:
这里dma_transfer_number_get的意思是还剩下多少个没接收到,例如DMA配置256个字节,那么接收了10个,那么dma_transfer_number_get就返回256-10=246.
然后我再用256 - 246 =10,那么10就是我接收到的数据的个数。
然后把数据复制出来,最后重新配配置接收DMA。
这样,只有当空闲的时候(两个位的时间 间隙),才进中断.
dma_USART_RX_NUM = dma_transfer_number_get(DMA0, DMA_CH1);
USART_RX_NUM = USART_MAX_LEN - dma_transfer_number_get(DMA0, DMA_CH1);
//空闲中断
extern "C" void USART2_IRQHandler(void)
{
uint8_t rxbuffer_real[USART_MAX_LEN];
dma_single_data_parameter_struct dma_init_struct;
static uint16_t USART_RX_NUM=0,dma_USART_RX_NUM=0;;
if((RESET != usart_interrupt_flag_get(USART2, USART_INT_FLAG_IDLE)) &&
(RESET != usart_flag_get(USART2, USART_FLAG_IDLE)))
{
dma_flag_clear(DMA0, DMA_CH1, DMA_INTF_FTFIF);
usart_data_receive(USART2); /* 清除接收完成标志位 */
dma_channel_disable(DMA0, DMA_CH1); /* 关闭DMA传输 */
dma_USART_RX_NUM = dma_transfer_number_get(DMA0, DMA_CH1);
USART_RX_NUM = USART_MAX_LEN - dma_transfer_number_get(DMA0, DMA_CH1);
mymemcpy(rxbuffer_real,Global_Usart1_Obj->usart_recebuf,USART_RX_NUM); /* 转存数据到待处理数据缓冲区*/
printf("%s\r\n",rxbuffer_real);
rxbuffer_real[USART_RX_NUM] = '\0'; /* 添加字符串结束符 */
/* 重新设置DMA传输 */
dma_deinit(DMA0, DMA_CH1);
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_init_struct.memory0_addr = (uint32_t)Global_Usart1_Obj->usart_recebuf;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.periph_memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = USART_MAX_LEN;
dma_init_struct.periph_addr = USART2_DATA_ADDRESS;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(DMA0, DMA_CH1, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH1);
dma_channel_subperipheral_select(DMA0, DMA_CH1, DMA_SUBPERI4);
/* enable DMA channel2 */
dma_channel_enable(DMA0, DMA_CH1);
usart_dma_receive_config(USART2, USART_DENR_ENABLE);
}
}
注意:这里有一个小BUG:
就是当一次性接收的数据大于你的DMA配置的个数。
例如配置了接收10个,可是别人发了10个 以上的数据,那么
情况1:如果一直发10个以上的话,那么数据会错乱,最后一个数据会覆盖第一个数据
情况2:如果之后是10个以内,那么第一次会错乱,然后之后就会正常。
这个原因可能是DMA溢出错误了,我没有去处理的原因 。
有一种办法是,把 DMA的配置个数改得很大,那么就不容易出现,
还有一种就是新加个DMA溢出中断.(还没实现)