这里用的是野火霸道的板子,搭载了STM32F103ZET6. 目前所学知识尚且较少,这里的笔记仅仅记录一下学习过程中的代码,方便以后回头复习以及复制。目前来说,似乎大部分代码都非常易懂,故注释较少。参考资料——《零死角玩转 STM32F103—霸道_V2 开发板》,参考视频——150集-野火F103霸道/指南者视频教程
部分原理图展示
按键监控点亮LED灯
利用GPIO监控按键电位,从而控制LED灯亮灭。GPIO模式区别
其他函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void Delay(__IO uint32_t nCount){ for(; nCount != 0; nCount--); }
uint8_t waitkey(GPIO_TypeDef* GPIOx, uint16_t GPIO_PIN){ if (GPIO_ReadInputDataBit(GPIOx, GPIO_PIN) == 1){ while(GPIO_ReadInputDataBit(GPIOx, GPIO_PIN) == 1); return 1; }else{ return 0; } }
void LED_TURN_ON(char i){ GPIO_SetBits(GPIOB, GPIO_Pin_All); if (i % 3 == 0){ GPIO_ResetBits(GPIOB, GPIO_Pin_5); }else if(i % 3 == 1){ GPIO_ResetBits(GPIOB, GPIO_Pin_0); }else if(i % 3 == 2){ GPIO_ResetBits(GPIOB, GPIO_Pin_1); } }
|
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| int main(void){ uint16_t COUNT = 0; GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13; GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5; GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_SetBits(GPIOB, GPIO_Pin_All);
while (1){ if (waitkey(GPIOA, GPIO_Pin_0) == 1){ GPIO_SetBits(GPIOB, GPIO_Pin_All); } if (waitkey(GPIOC, GPIO_Pin_13) == 1){ LED_TURN_ON(COUNT++); } } }
|
中断
中断函数的名称可以在启动文件内接近尾端部分查到。关于Interrupt Numbers,External Line 04的写法基本一致:EXTI0_IRQn
EXTI4_IRQn
,但Line 59和Line 1015写为EXTI9_5_IRQn
和EXTI15_10_IRQn
.
宏部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA #define KEY1_GPIO_PORT GPIOA #define KEY1_GPIO_PIN GPIO_Pin_0 #define KEY1_EXTI_PORTSRC GPIO_PortSourceGPIOA #define KEY1_EXTI_PINSRC GPIO_PinSource0 #define KEY1_EXTI_LINE EXTI_Line0 #define KEY1_EXTI_IRQ EXTI0_IRQn #define KEY1_IRQHandler EXTI0_IRQHandler
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC #define KEY2_GPIO_PIN GPIO_Pin_13 #define KEY2_GPIO_PORT GPIOC #define KEY2_EXTI_PORTSRC GPIO_PortSourceGPIOC #define KEY2_EXTI_PINSRC GPIO_PinSource13 #define KEY2_EXTI_LINE EXTI_Line13 #define KEY2_EXTI_IRQ EXTI15_10_IRQn #define KEY2_IRQHandler EXTI15_10_IRQHandler
#define LED_GPIO_CLK RCC_APB2Periph_GPIOB #define LED_GPIO_PORT GPIOB #define LED_R_GPIO_PIN GPIO_Pin_5 #define LED_G_GPIO_PIN GPIO_Pin_0 #define LED_B_GPIO_PIN GPIO_Pin_1
|
核心函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| static void EXTI_NVIC_Config(void){ NVIC_InitTypeDef NVIC_InitStruct; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStruct.NVIC_IRQChannel = KEY1_EXTI_IRQ; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = KEY2_EXTI_IRQ; NVIC_Init(&NVIC_InitStruct); }
void EXTI_Key_Config(void){ GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct;
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); EXTI_NVIC_Config();
GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStruct); GPIO_EXTILineConfig(KEY1_EXTI_PORTSRC, KEY1_EXTI_PINSRC); EXTI_InitStruct.EXTI_Line = KEY1_EXTI_LINE; EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStruct); GPIO_InitStruct.GPIO_Pin = KEY2_GPIO_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStruct); GPIO_EXTILineConfig(KEY2_EXTI_PORTSRC, KEY2_EXTI_PINSRC); EXTI_InitStruct.EXTI_Line = KEY2_EXTI_LINE; EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStruct); }
void KEY1_IRQHandler(void){ if (EXTI_GetITStatus(KEY1_EXTI_LINE) != RESET) { count--; LED_TURN_ON(count); EXTI_ClearITPendingBit(KEY1_EXTI_LINE); } }
void KEY2_IRQHandler(void){ if (EXTI_GetITStatus(KEY2_EXTI_LINE) != RESET) { count++; LED_TURN_ON(count); EXTI_ClearITPendingBit(KEY2_EXTI_LINE); } }
void LED_init(void){ GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE); GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Pin = LED_R_GPIO_PIN; GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN; GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = LED_B_GPIO_PIN; GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct); GPIO_SetBits(LED_GPIO_PORT, GPIO_Pin_All); }
void LED_TURN_ON(char i);
|
主函数
1 2 3 4 5 6
| char count = 0; int main(void){ LED_init(); EXTI_Key_Config(); while(1){} }
|
SysTick
24bit递减计数器,每计数一次用时为1/SYSCLK,一般SYSCLK等于72M。SysTick属于内核外设,有关的寄存器定义和库函数都在内核相关的库文件core_cm3.h
中。SysTick属于内核外设,跟普通外设的中断优先级有区别,没有抢占优先级和子优先级的说法,其优先级只需要配置一个寄存器即可,取值范围为0000b~1111b。但是中断优先级分组依旧对其起作用,即配置优先级方式不同,但优先级划分方式还是跟分组的一致。
下面这一段代码在我的机器上似乎有点问题,一执行就会卡死。试过在while循环下面添加点亮LED,但是始终没有成功点亮,说明没有跳出循环,这貌似是CRTL寄存器位16始终没有置1。尝试扩展第6行,在while循环体中输出SysTick->VAL
,但是只会输出两次VAL
的值,然而并未跳出Delay_us执行后面的内容。
1 2 3 4 5 6 7 8 9 10
| void Delay_us( __IO uint32_t us){ uint32_t i; SysTick_Config(SystemCoreClock/1000000); for (i=0; i<us; i++) { while ( !((SysTick->CTRL)&(1<<16)) ); } SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; }
|
另一种方式,这种无问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| static __IO u32 TimingDelay;
void Delay_ms(__IO u32 nTime){ SysTick_Config(SystemCoreClock / 1000); SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; TimingDelay = nTime; SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; while(TimingDelay != 0); }
void SysTick_Handler(void){ if (TimingDelay != 0) TimingDelay--; else SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; }
|
还有一种方式,在B站视频底下看到的,还未测试。
1 2 3 4 5 6 7 8 9 10 11
| void delay_us(uint32_t nus){ uint32_t temp; SysTick->LOAD=9*nus; SysTick->VAL=0x00; SysTick->CTRL=0x01; do{ temp=SysTick->CTRL; }while((temp&0x01)&&(!(temp&(1<<16)))); SysTick->CTRL=0x00; SysTick->VAL=0x00; }
|
USART收发数据 (中断式)
同步异步收发器 (Universal Synchronous Asynchronous Receiver and Transmitter, USART)
USB转串口部分原理图
宏部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #define USARTx USART1 #define USART_CLK RCC_APB2Periph_USART1 #define USART_CLK_Cmd RCC_APB2PeriphClockCmd #define USART_BAUDRATE 115200
#define USART_GPIO_CLK RCC_APB2Periph_GPIOA #define USART_GPIO_CLK_Cmd RCC_APB2PeriphClockCmd
#define USART_TX_GPIOx GPIOA #define USART_TX_GPIO_PIN GPIO_Pin_9 #define USART_RX_GPIOx GPIOA #define USART_RX_GPIO_PIN GPIO_Pin_10
#define USART_IRQ USART1_IRQn #define USART_IRQHandler USART1_IRQHandler
|
核心函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| static void NVIC_Config(void){ NVIC_InitTypeDef NVIC_InitStruct; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStruct.NVIC_IRQChannel = USART_IRQ; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); }
void USART_Config(void){ GPIO_InitTypeDef GPIO_InitStruct; USART_InitTypeDef USART_InitStruct; USART_GPIO_CLK_Cmd(USART_GPIO_CLK, ENABLE); USART_CLK_Cmd(USART_CLK, ENABLE); GPIO_InitStruct.GPIO_Pin = USART_TX_GPIO_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(USART_TX_GPIOx, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = USART_RX_GPIO_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(USART_RX_GPIOx, &GPIO_InitStruct); USART_InitStruct.USART_BaudRate = USART_BAUDRATE; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USARTx, &USART_InitStruct); NVIC_Config(); USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE); USART_Cmd(USARTx, ENABLE); }
void USART_Send_Byte(USART_TypeDef* pUSARTx, uint8_t data){ USART_SendData(pUSARTx, data); while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET ); }
void USART_Send_Str( USART_TypeDef * pUSARTx, char *str){ uint32_t k = 0; while ( *(str+k) != '\0' ){ USART_Send_Byte(pUSARTx, *(str+(k++)) ); } while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET); }
|
中断服务函数
1 2 3 4 5 6 7
| void USART_IRQHandler(void){ uint8_t data; if (USART_GetFlagStatus(USARTx, USART_IT_RXNE) != RESET){ data = USART_ReceiveData(USARTx); USART_SendData(USARTx, data); } }
|
主函数
1 2 3 4 5
| int main(void){ USART_Config(); USART_Send_Str(USARTx, "Hello World"); while(1); }
|
Note: 有BUG,无法接收数据,原因尚不清楚,只知道中断服务函数并未调用。 主函数最后加上while(1);
即可,不加的话,貌似会进入HARDFAULT,导致中断服务函数无法执行。
USART 通信控制LED (标准库)
头部记得包含#include <stdio.h>
。这一部分为上一部分略作修改。
关闭USART_Config()中的中断相关部分
重写fputc/fgetc
1 2 3 4 5 6 7 8 9 10
| int fputc(int ch, FILE *f){ USART_SendData(USARTx, (uint8_t)ch); while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET ); return ch; }
int fgetc(FILE *f){ while(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET); return (int32_t) USART_ReceiveData(USARTx); }
|
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| int main(void){ char show; LED_init(); USART_Config(); printf("Hello world\n"); while(1){ show = getchar(); printf("#: %c\n", show); switch (show){ case 'r': LED_TURN_ON(0); break; case 'g': LED_TURN_ON(1); break; case 'b': LED_TURN_ON(2); break; default: printf("undefined.\n"); } } }
|
DMA+USART发送数据
DMA控制器独立于内核,属于一个单独的外设,主要是用来搬数据,且不占用CPU。不同的DMA控制器的通道对应着不同的外设请求, 应当根据DMA请求映像表选择通道。当发生多个DMA通道请求时,由仲裁器管理响应处理的顺序。
宏部分
1 2 3 4
| #define USART_TX_DMA_CHAN DMA1_Channel4 #define USART_DR_ADDR (u32) &USART1->DR #define BUFFER_SIZE 1024
|
核心函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| uint32_t send_buff[BUFFER_SIZE];
void USARTx_DMA_Config(void){ DMA_InitTypeDef DMA_InitStruct; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_InitStruct.DMA_PeripheralBaseAddr = USART_DR_ADDR; DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)send_buff; DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE; DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; DMA_InitStruct.DMA_Priority = DMA_Priority_High; DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; DMA_Init(USART_TX_DMA_CHAN, &DMA_InitStruct); DMA_Cmd(USART_TX_DMA_CHAN, ENABLE); }
|
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| int main(void){ uint32_t i; LED_init(); USART_Config(); USARTx_DMA_Config(); for(i = 0; i < BUFFER_SIZE; i++){ send_buff[i] = 'a'; } USART_DMACmd(USARTx, USART_DMAReq_Tx, ENABLE); LED_TURN_ON(0); Delay_ms(500); LED_TURN_ON(1); Delay_ms(500); for(i = 0; i < BUFFER_SIZE; i++){ send_buff[i] = 'b'; } USART_DMACmd(USARTx, USART_DMAReq_Tx, DISABLE); DMA_DeInit(USART_TX_DMA_CHAN); USARTx_DMA_Config(); USART_DMACmd(USARTx, USART_DMAReq_Tx, ENABLE); LED_TURN_ON(0); Delay_ms(500); LED_TURN_ON(1); Delay_ms(500); }
|
其他
最初用keil5的时候就出了一堆问题,然后换keil4用了一段时间。感觉keil4的代码补全有点问题,于是我想到要用vscode配合Keil-Assistant插件来敲代码,可惜Keil-Assistant只支持Keil μVison 5及以上版本,我用新的Keil5可以正常编译,但是死活无法烧录,烧录时IDE只会输出Error: Flash Download failed - "Cortext-M3"
。根据百度的结果,以及问了好几位大哥,都无法解决问题,最后我换了MDK520版本。
关于MDK与C51共存
参考链接:
MDK和各种pack软件包镜像下载
Keil历史版本的几种下载方法