巨大八爪鱼
武林盟主 二十一级
|
#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; }
|
巨大八爪鱼
武林盟主 二十一级
|
主程序是一个数码管动态扫描的程序。DMA独立于CPU,不影响CPU当前执行的任务。
|
巨大八爪鱼
武林盟主 二十一级
|
这里要注意的是,CPAR是设置外设的地址,CMAR是设定内存的地址。DMA_CCR4_DIR决定是从外设中还是从内存中读取数据。
|
巨大八爪鱼
武林盟主 二十一级
|
【库函数版】 #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); } }
|