串口通信介绍
UART串口通信,使用三线即可进行最基本的数据收发传送:
在数据线上的 Timing 遵循标准的串口通信协议,由起始位,数据,校验位,停止位组成,数据传输 LSB -> MSB:
板载 USART 资源介绍
当然,由于电平不一样,使用 RS232 标准进行串口数据传送,需要增加 MAX3232 进行电平转换,再接PC:
单板上的 T1IN 和 R1OUT 接到了 STM32 芯片的 USART1 的 TXD/RXD 管脚,故单板上使用了 USART1 来作为 RS232 和 PC 机进行数据传送:
USART 初始化配置
既然确定了使用了芯片上的 USART1,要正确使用该功能,需要进行如下配置:
1. 开启 USART1 时钟源,开启 GPIOA 组时钟源(因为使用 UASRT1之前,对 PA9/PA10 需要对管脚进行配置)
2. 复位 USART1 模块(使用之前,应当首先对该模块进行复位)
3. 配置管脚功能的 Remap
4. 配置 PA9/PA10 管脚,PA9 为 TXD,配置成为推挽输出,PA10 配置成为浮空输入
5. 配置串口的波特率(9600),数据长度(8bit),停止位(1bit),校验位(无),以及是否开启流控(无)
注意:波特率的配置,遵循一组计算公式(公式复杂),详见 STM32 的芯片手册
6. 配置 NVIC 控制器
7. 配置 USART1 的中断类型(即,工作过程中,会来些什么中断)
8. 使能 USART1 功能
void SK_UartInit(void)
{
GPIO_InitTypeDef stGpioInit;
USART_InitTypeDef stUsartInit;
NVIC_InitTypeDef stNVIC;
/* Step1: Open USART1 And GPIOA Clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
USART_DeInit(USART1);
/* Step2: Config The Pin Remap */
// According to the hardware diagram, just use UART1 On PA9 And PA10 in default
GPIO_PinRemapConfig(GPIO_Remap_USART1, DISABLE);
/* Step3: Config RXD/TXD Mode */
// PA9 As TXD
stGpioInit.GPIO_Pin = GPIO_Pin_9;
stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;
stGpioInit.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &stGpioInit);
// PA10 As RXD
stGpioInit.GPIO_Pin = GPIO_Pin_10;
stGpioInit.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &stGpioInit);
/* Step4: Reset USART1 before use it */
USART_DeInit(USART1);
/* Step5: Configure the UART Basic Settings */
stUsartInit.USART_BaudRate = 9600;
stUsartInit.USART_WordLength = USART_WordLength_8b;
stUsartInit.USART_StopBits = USART_StopBits_1;
stUsartInit.USART_Parity = USART_Parity_No;
stUsartInit.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
stUsartInit.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &stUsartInit);
// Configure RX Interrupt
stNVIC.NVIC_IRQChannel = USART1_IRQn;
stNVIC.NVIC_IRQChannelPreemptionPriority= 3 ;
stNVIC.NVIC_IRQChannelSubPriority = 3;
stNVIC.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&stNVIC);
#ifndef USART_USE_DMA
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
#endif
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
// Enable Usart1
USART_Cmd(USART1, ENABLE);
}
在配置过程中,只打开了 RX 接收中断
TX 发送数据时刻,只需 polling 是否发送完成,在进行下一次的数据发送即可。
值得注意的是,数据收发都是使用了同一个DR寄存器,不同时刻,由硬件来进行区分。
USART 数据 TX 发送(Polling)
STM32 USART 数据发送是通过往 USART 的 DR 寄存器写值完成的,DR 寄存器支持每次写 1 Byte 的数据,每次写完数据后,硬件会将 DR 寄存器的值送到移位寄存器中,将数据发送出去。软件需要 polling 硬件的 TC (Transfer Complete)标志位,待 1 Byte 数据发送完成后,再次进行下一个数据的发送:
void SK_UsartSendChar(uint8_t ch)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
USART_SendData(USART1, ch);
}
void SK_UsartSendData(uint8_t *buf, uint32_t len)
{
uint32_t i = 0;
for (i = 0; i < len; i++)
SK_UsartSendChar(buf[i]);
}
USART 数据 RX 接收(IRQ)
对于数据接收,使用 Polling 显然不是一种好办法,用中断的方式进行数据接收,接收的数据带简单的自定义格式:帧头+长度+Data的方式:
typedef struct {
volatile uint32_t hdr_found;
volatile uint32_t data_len;
volatile uint32_t data_ready;
}SK_USART_RX_CTL_t;
SK_USART_RX_CTL_t g_stUsartRxCtl;
uint8_t g_SK_UsartRxDataBuf[SK_USART_RX_BUF_LEN] = {0};
/*
* Protocol: 1st. Frame start at 0x5A
* 2nd. Second is data length
* 3rd. Data
*/
/*
void USART1_IRQHandler(void)
{
uint8_t rx_data = 0;
static uint8_t cnt = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE))
{
rx_data = USART_ReceiveData(USART1);
}
if (SK_USART_RX_FRM_HEADER == rx_data && !g_stUsartRxCtl.hdr_found)
{
g_stUsartRxCtl.hdr_found++;
return;
}
if (g_stUsartRxCtl.hdr_found && g_stUsartRxCtl.data_len == 0)
{
g_stUsartRxCtl.data_len = rx_data;
if (g_stUsartRxCtl.data_len > SK_USART_RX_BUF_LEN)
g_stUsartRxCtl.data_len = SK_USART_RX_BUF_LEN;
return;
}
if (cnt < g_stUsartRxCtl.data_len)
{
g_SK_UsartRxDataBuf[cnt++] = rx_data;
if (g_stUsartRxCtl.data_len == cnt)
{
g_stUsartRxCtl.data_ready = 1;
cnt = 0;
SK_SetLedStatus(SK_LED_1, SK_LED_ON);
delay_ms(2000);
SK_SetLedStatus(SK_LED_1, SK_LED_OFF);
SK_UsartSendData(g_SK_UsartRxDataBuf, g_stUsartRxCtl.data_len);
}
}
}
USART 数据 RX 接收(IRQ + DMA)
虽然可以使用 IRQ 的方式进行一个 Byte 一个 Byte 的数据接收(中断),但您不觉得这会让 CPU 累死么?看着都费劲。
好嘛,STM32 UASRT 数据接收又不带 FIFO,不过没关系,用 DMA 放飞 CPU 吧!!
STM32 的 DMA1 支持 7个通道:
如上图所示:Ch4 用作 USART1_TX,Ch5 用作 USART1_RX。让 CPU 在歇一会。转存失败重新上传取消
USART DMA 配置
DMA 指的是 (Direct Memory Access)直接内存存取,不经过 CPU。STM32 的 DMA 支持外设到内存,内存到外设,以及内存到内存。只要咱们告诉 DMA 控制器,从什么地方去取外设数据,数据有多少,数据宽度是多少,以及将数据放置到内存的什么地方,它便可以带你飞。
当然,DMA 也支持中断的配置,能够配置成为数据传送一半的时候来中断,or,数据传送完来中断,从各方面解决了您的烦恼。让您无忧无虑进行数据传送。
回到正题上来,配置 USART DMA要有入下几个步骤:
1. 开启 DMA1 时钟(这不废话么)
2. 配置 NVIC,并使能(也是废话)
3. 复位 DMA1 的 Ch4/Ch5
4. 设定外设地址为 USART1 的 DR 寄存器,即数据寄存器
5. 设置接收数据的内存地址(本地的一个缓存 RX BUF指针)
6. 设置数据方向为 USART1 的 DR 寄存器 ----> 内存
7. 设置 DMA 传输的数据大小(最大 65536)
8. 设置关闭外设地址自动增加
9. 设置启用缓存 BUF 地址自动增加 (数据传来后,自动存在本地 RX BUF 并指针递增)
10. 配置外设传输数据宽度为 8bit (USART DR 寄存器就 8bit)
11. 配置本地缓存数据宽度为 8bit
12. 配置 DMA 传输模式为 one shot(即传输完一次后,就停止了,也可以配置成为循环模式)
13. 设置 DMA CH15 的优先级
14. 关闭 memory to memory(废话,使用的是外设到内存的数据传输)
15. 开启 DMA1 CH15
16. 使能数据传输完成的 DMA1 中断
17. 在 USART 寄存器中,开启 RX DMA 请求(此项是在 USART 寄存器中进行配置)
好啦,此刻配置基本完成:
void SK_UsartDmaInit(void)
{
DMA_InitTypeDef stDMA_InitStructCh4;
DMA_InitTypeDef stDMA_InitStructCh5;
/// Step 1 : Open the DMA1 Clock
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// NVIC Config
DMA_NVIC_Config();
/// Step 2 : Reset DMA1_CH4(For USART1 TX) and DMA1_CH5(For USART1 RX)
DMA_DeInit(DMA1_Channel4);
// Configure the USART1 TX DMA Transfer for DMA CH4
// Configure the Peripheral address
stDMA_InitStructCh4.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
// Configure the TX Data Buffer address
stDMA_InitStructCh4.DMA_MemoryBaseAddr = (uint32_t)g_SK_UsartTxDataBuf;
// Configure the data direct: Memory to Peripheral
stDMA_InitStructCh4.DMA_DIR = DMA_DIR_PeripheralDST;
// Configure the data Len
stDMA_InitStructCh4.DMA_BufferSize = SK_USART_TX_BUF_LEN;
// Configure the Peripheral Address auto add (disable)
stDMA_InitStructCh4.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// Configure the Memory Address auto add (enable)
stDMA_InitStructCh4.DMA_MemoryInc = DMA_MemoryInc_Enable;
// Configure the Peripheral Data Size = 1 byte
stDMA_InitStructCh4.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
// Configure the Memory Data Size = 1 byte
stDMA_InitStructCh4.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
// Configure Normal mode
stDMA_InitStructCh4.DMA_Mode = DMA_Mode_Normal;
// Configure Priority as medium
stDMA_InitStructCh4.DMA_Priority = DMA_Priority_Medium;
// Disable memory to memory
stDMA_InitStructCh4.DMA_M2M = DMA_M2M_Disable;
// Config DMA1 CH4
DMA_Init(DMA1_Channel4, &stDMA_InitStructCh4);
// Enable Ch4
DMA_Cmd(DMA1_Channel4, ENABLE);
// Enable IRQ when transfer finished
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
/// Step 3: Reset DMA1_CH5(For USART1 RX)
DMA_DeInit(DMA1_Channel5);
// Configure the USART1 RX DMA for DMA CH5
// Configure the Peripheral address
stDMA_InitStructCh5.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
// Configure the TX Data Buffer address
stDMA_InitStructCh5.DMA_MemoryBaseAddr = (uint32_t)g_SK_UsartRxDataBuf;
// Configure the data direct: Memory to Peripheral
stDMA_InitStructCh5.DMA_DIR = DMA_DIR_PeripheralSRC;
// Configure the data Len
stDMA_InitStructCh5.DMA_BufferSize = SK_USART_RX_BUF_LEN;
// Configure the Peripheral Address auto add (disable)
stDMA_InitStructCh5.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
上一篇:STM32F103ZET6 启动模式
下一篇:STM32F103ZET6 — ADC
推荐阅读
史海拾趣
Adafruit Industries是一家总部位于美国纽约的开源硬件公司,致力于设计和制造创意电子产品。以下是该公司发展的五个相关故事:
公司创立与初期阶段: Adafruit Industries由Limor Fried于2005年创立,起初是一个个人项目。Limor Fried(也被称为Ladyada)是一位热衷于开源硬件和电子制作的工程师,她在创办Adafruit之前就已经是开源硬件社区的活跃成员。最初,Adafruit专注于销售自制的电子配件和模块,并提供相关的教育资源和项目指南。
开源文化的推动者: Adafruit是开源硬件运动的积极推动者之一,致力于促进开源硬件的发展和普及。公司提倡知识共享和技术开放,通过在GitHub上发布开源硬件项目和提供详细的教程,鼓励更多人参与到电子制作和创客活动中来。
产品线的不断扩展: 随着市场需求的增长和公司规模的扩大,Adafruit逐渐扩展了产品线,涵盖了各种电子配件、传感器、开发板等。公司还推出了一系列DIY电子套件,旨在帮助用户学习电子制作和编程技能。Adafruit的产品以其高品质和易用性而著称,受到了全球创客和电子爱好者的青睐。
教育和社区建设: Adafruit积极参与教育和社区建设工作,通过在线教程、视频教程、工作坊等方式,向学生和爱好者传授电子知识和技能。公司还定期举办各种活动和比赛,鼓励创客社区的互动和交流,推动创新和创意的产生。
持续创新和发展: 作为一家持续创新的公司,Adafruit不断推出新产品和解决方案,不断满足客户不断增长的需求。公司还与各种组织和机构合作,推动开源硬件的发展和应用,努力成为开源硬件领域的领先者和推动者。随着时间的推移,Adafruit将继续秉承其开源和创新的精神,为全球创客社区带来更多的惊喜和机会。
在21世纪初,全球半导体行业蓬勃发展,台湾地区的IC设计领域也呈现出勃勃生机。在这样的背景下,晶发半导体的创始人蒲文豪怀揣着对低功率SRAM领域的深厚热情,于2002年创立了Chiplus Semiconductor Corp.(晶发半导体)。蒲文豪曾在一家专攻低功率SRAM的公司工作,深知这一领域的潜力和市场需求。他立志要做低功率SRAM领域的领军者,为全球客户提供卓越的产品和服务。
随着技术的成熟和产品的完善,Cornerstone Sensors开始积极拓展市场。公司参加了多个国际电子展会和技术研讨会,与全球各地的潜在客户和合作伙伴建立了联系。凭借卓越的产品性能和专业的技术支持,Cornerstone Sensors赢得了众多客户的信任,并成功打入了国际市场。同时,公司还积极寻求与上下游企业的合作,共同推动传感器技术的发展和应用。
为了提升产品质量,增强客户信任,灿科盟在2008年成功通过了ISO9001:2000版质量管理体系认证。这一认证标志着公司在质量管理方面达到了国际标准,也为公司赢得了更多客户的青睐。此后,公司继续加强质量管理体系建设,不断提升产品质量和服务水平。
为了进一步深耕中国市场并加强本土化战略的实施,依必安派特在2024年宣布正式启用其大中华区新总部“一个上海”(ONE Shanghai)。这一新总部占地33000平方米,将原本分散在上海的四处基地整合到了新总部。这一举措不仅提高了公司的运营效率和管理水平,也进一步强化了依必安派特在中国市场的创新领导地位和长期发展态势。新总部的启用标志着依必安派特在中国市场的又一次飞跃发展,为其未来的发展奠定了坚实基础。
1996年,依必安派特在上海外高桥保税区设立了其在中国的首个据点——依必安派特风机(上海)有限公司。最初,它仅作为销售为主的贸易公司,为中国市场提供德国制造的风机和电机产品。然而,随着中国市场的快速增长和需求的不断变化,依必安派特意识到仅仅作为贸易公司已无法满足市场需求。于是,在2000年,依必安派特电气(上海)有限公司正式成立,开始在上海本地生产风机和电机,标志着依必安派特从贸易到制造的转变。
模块的ioctl段的定义函数是: int camif_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { camif_cfg_t *cfg = file->private_data; struct ...… 查看全部问答∨ |
wincepb50-051231-product-update-rollup-armv4i wincepb50-061231-product-update-rollup-armv4i wincepb50-071231-product-update-rollup-armv4i 这3个补丁依次包含吗? 是不是打07的以后就不用打上面2个了… 查看全部问答∨ |
用EVC5.0做绘制实时数据曲线(每秒采集一次数据),为何不能自动刷新? 用EVC5.0做绘制实时数据曲线(每秒采集一次数据),运行时为何不能自动刷新? 而在VC下编译、运行却没有任何问题。… 查看全部问答∨ |
请问一下有没有正在使用VHDL语言的吗?我想请教一下问题,用VHDL编写一个程序后,再运行,会生成一个项目符号 , 在图形输入文件中如何才能调用它啊? 有谁知道的给指点一下,谢谢!!… 查看全部问答∨ |