目前共有4篇帖子。 內容轉換:不轉換▼
 
點擊 回復
698 3
【程序】STM32通过DMA自动向串口发送指定长度的数据
一派護法 十九級
1樓 發表于:2017-1-16 18:02
#include <stm32f10x.h>
#include <string.h>

#define _BV(n) (1 << (n))

uint32_t num = 12345678;
const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};

void delay(void)
{
    uint16_t i;
    for (i = 0; i < 20000; i++);
}

void ser_in(uint8_t data)
{
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        GPIOB->BRR = GPIO_BRR_BR9; // SCLK=>PB9
        if (data & 0x80)
            GPIOB->BSRR = GPIO_BSRR_BS7; // DIO=>PB7
        else
            GPIOB->BRR = GPIO_BRR_BR7;
        data <<= 1;
        GPIOB->BSRR = GPIO_BSRR_BS9;
    }
}

void par_out(void)
{
    GPIOB->BRR = GPIO_BRR_BR8; // RCLK=>PB8
    GPIOB->BSRR = GPIO_BSRR_BS8;
}

void seg_scan(void)
{
    uint8_t i;
    uint32_t n = num;
    for (i = 0; i <= 7; i++)
    {
        ser_in(seg8[n % 10]);
        ser_in(_BV(i));
        par_out();
        delay();
        n /= 10;
    }
}

int main(void)
{
    const char *str = "By choosing one of ST's microcontrollers for your embedded application, you gain from our leading expertise in MCU architecture, technology, multi-source manufacturing and support.\n";
    
    // 配置数码管输出端口
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    GPIOB->CRL = 0x30000000; // PB7~9设为输出
    GPIOB->CRH = 0x00000033;
    
    // 配置串口
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
    GPIOA->CRH = 0x000008b0; // PA9(TX)设为复用50MHz推挽输出, PA10(RX)设为输入
    GPIOA->BSRR = GPIO_BSRR_BS10; // PA10带上拉输入
    USART1->BRR = 0x1d4c; // 波特率9600
    USART1->CR1 |= USART_CR1_UE | USART_CR1_RE | USART_CR1_TE; // 打开串口, 允许发送和接收
    USART1->CR3 |= USART_CR3_DMAT; // 允许通过DMA发送数据
    
    // 配置DMA
    RCC->AHBENR |= RCC_AHBENR_DMA1EN; // 开DMA1时钟
    DMA1_Channel4->CNDTR = strlen(str); // 数据大小为字符串的长度
    DMA1_Channel4->CPAR = (uint32_t)&USART1->DR; // 目的地址为串口的DR寄存器(外设地址)
    DMA1_Channel4->CMAR = (uint32_t)str; // 源地址为要发送的字符串(内存地址)
    DMA1_Channel4->CCR |= DMA_CCR4_EN | DMA_CCR4_TCIE | DMA_CCR4_MINC | DMA_CCR4_DIR; // 打开DMA通道及其中断, 内存地址str自动增加, 方向为从内存中读取
    NVIC->ISER[DMA1_Channel4_IRQn / 32] |= _BV(DMA1_Channel4_IRQn % 32);
    
    while (1)
        seg_scan();
}

// DMA传输完毕中断
void DMA1_Channel4_IRQHandler(void)
{
    DMA1->IFCR |= DMA_IFCR_CTCIF4;
    num = 87654321;
}
一派護法 十九級
2樓 發表于:2017-1-16 18:02
主程序是一个数码管动态扫描的程序。DMA独立于CPU,不影响CPU当前执行的任务。
一派護法 十九級
3樓 發表于:2017-1-16 18:05
这里要注意的是,CPAR是设置外设的地址,CMAR是设定内存的地址。DMA_CCR4_DIR决定是从外设中还是从内存中读取数据。
一派護法 十九級
4樓 發表于:2017-1-16 23:08
【库函数版】
#include <stm32f10x.h>

void delay(void)
{
    uint32_t i;
    for (i = 0; i < 2000000; i++);
}

void config_gpio(void)
{
    GPIO_InitTypeDef gpio;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
   
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; // LED灯
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio);
   
    gpio.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio.GPIO_Pin = GPIO_Pin_9; // TX
    GPIO_Init(GPIOA, &gpio);
   
    gpio.GPIO_Mode = GPIO_Mode_IPU;
    gpio.GPIO_Pin = GPIO_Pin_10; // RX
    GPIO_Init(GPIOA, &gpio);
}

void config_usart(void)
{
    USART_InitTypeDef usart;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
   
    USART_StructInit(&usart); // 直接采用默认的串口参数
    USART_Init(USART1, &usart);
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); // 允许通过DMA发送数据
    USART_Cmd(USART1, ENABLE);
}

// 用DMA方式发送批量数据
void usart_send_data(void *data, uint16_t size)
{
    DMA_InitTypeDef dma;
    DMA_StructInit(&dma);
    dma.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    dma.DMA_MemoryBaseAddr = (uint32_t)data;
    dma.DMA_DIR = DMA_DIR_PeripheralDST; // 从主存到外设
    dma.DMA_BufferSize = size;
    dma.DMA_MemoryInc = DMA_MemoryInc_Enable; // 主存地址自动后移
    DMA_Init(DMA1_Channel4, &dma);
    DMA_Cmd(DMA1_Channel4, ENABLE);
}

int main(void)
{
    config_gpio();
    config_usart();
   
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 开机时必须等到TC=1后才能发送数据
    USART_SendData(USART1, 'a');
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 等待字符发送完毕
    USART_SendData(USART1, '\n');
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
    usart_send_data("Hello World!\n", 13); // 用DMA方式发送
   
    while (1)
    {
        delay();
        GPIO_SetBits(GPIOB, GPIO_Pin_8);
        delay();
        GPIO_ResetBits(GPIOB, GPIO_Pin_8);
    }
}

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:698 回複數:3
評論數: ?
作者:巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2017-1-16 23:08
 
©2010-2024 Arslanbar Ver2.0
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。