目录
一、GPIO
1.时钟的配置
cpp
//开启引脚的时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
2.设置GPIO的工作模式
cpp
//PA0的工作模式为通用推挽输出模式
//CNF选择输入或输出的不同模式
GPIOA->CRL &= ~GPIO_CRL_CNF0;
//MODE选择引脚为输出或输入,以及输出的频率
GPIOA->CRL |= GPIO_CRL_MODE0;
3.设置引脚的电平
当引脚为输出时进行设置
cpp
//将PA0设置为输出低电平
GPIOA->ODR &= ~GPIO_ODR_ODR0;
二、中断系统
1.时钟的配置
cpp
//开启引脚的时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPFEN;
//开启AFIO引脚复用选择器的时钟
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
2.设置GPIO的工作模式
cpp
//将PF10设置为上下拉输入模式
GPIOF->CRH &= ~GPIO_CRH_MODE10;
GPIOF->CRH |= GPIO_CRH_CNF10_1;
GPIOF->CRH &= ~GPIO_CRH_CNF10_0;
//设置PF10的电平为低电平,确认其为下拉输入模式
GPIOF->ODR &= ~GPIO_ODR_ODR10;
3. 配置AFIO引脚复用
cpp
//设置AFIO的第10个引脚为PF10复用
AFIO->EXTICR[2] |= AFIO_EXTICR3_EXTI10_PF;
4.EXTI的配置
cpp
//设置为上升沿触发中断
EXTI->RTSR |= EXTI_RTST_TR10;
//开启10号引脚的中断请求
EXTI->IMR |= EXTI_IMR_MR10;
5.NVIC的配置
cpp
//通过调用函数,简化配置
//设置优先级组
NVIC_SetPriorityGrouping(3);
//设置10传输中断请求的优先级
NVIC_SetPriority(EXTI15_10_IRQn,3);
//开启中断请求
NVIC_EnableIRQ(EXTI15_10_IRQn);
6.中断处理程序
cpp
//函数名已经定义过,直接重写
void EXTI15_10_IRQHandler(void)
{
//首先清除中断标志位
EXTI->PR |= EXTI_PR_PR10;
/* 执行中断的代码 */
}
三、USART串口通信
1.时钟的配置
cpp
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
//开启串口的时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
2.设置GPIO的工作模式
cpp
//配置输出引线,为复用推挽输出;
GPIOA->CRH |= GPIO_CRH_MODE9;
GPIOA->CRH |= GPIO_CRH_CNF9_1;
GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
//配置输入引线,为浮空输入
GPIOA->CRH &= ~GPIO_CRH_MODE10;
GPIOA->CRH |= GPIO_CRH_CNF10_0;
GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
3.串口配置
cpp
//直接给BRR寄存器赋值
USRAT1->BRR = 0x271;
//开启各种使能
//开启USART1的使能
USART1->CR1 |= USART_CR1_UE;
//开启发送和接收使能
USART1->CR1 |= USART_CR1_TE;
USART1->CR1 |= USART_CT1_RE;
//设置数据帧的格式
//数据帧的有效数据为8位
USART1->CR1 &= ~USART_CR1_M;
//关闭校验位,开启则有效位置1,变为9位
USART1->CR1 &= ~USART_CR1_PCE;
//配置停止位
USART1->CR2 &= ~USART_CR2_STOP;
//开启中断使能
//空闲线路中断开启
USART1->CR1 |= USART_CR1_IDLEIE;
//接收非空中断开启
USART1->CR1 |= USART_CR1_RXNEIE;
4.NVIC的配置
cpp
NVIC_SetPriorityGrouping(3);
NVIC_SetPriority(USART1_IRQn,3);
NVIC_EnableIRQ(USART1_IRQn);
5.收发数据
cpp
//发送数据
//采用轮询的方式进行发送数据
void USART_SendChar(uint8 data){
//轮询判断SR中TXE是否为1,表示是否发送完毕
while((USART1->SR & USART_SR_TXE) == 0)
{}
//发送完,则添加数据
USART1->DR = data;
}
//接收数据
//采用中断方式
void USART1_IRQHandler(void){
//接收非空时读取数据
if(USART1->SR & USART_SR_RXNE){
USART1->DR;
}
//线路空闲,接收完毕清除IDLE标志位
else if(USART1->SR & USART_SR_IDLE)
{
USART1->DR;
}
}
四、I2C通讯
1.时钟的配置
cpp
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
//开启I2C2的时钟
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
2.设置GPIO的工作模式
cpp
//将引脚设置为复用开漏输出
GPIOB->CRH |= (GPIO_CRH_MODE10 | GPIO_CRH_MODE11 |GPIO_CRH_CNF10 |
GPIO_CRH_MCNF11);
3.I2C2的配置
cpp
//选择硬件模式为I2C模式
I2C2->CR1 &= ~I2C_CR1_SMBUS;
//选择传输速度,为标志模式
I2C2->CCR &= ~I2C_CCR_FS;
//配置其输入的时钟频率
I2C2->CR2 |= 36;
//配置CCR,高电平的占比时间,对应数据传输速率100kb/s,高电平时间为5us
I2C2->CCR |= 180; //为36 * 5
//配置TRISE,SCL上升沿最大时钟周期数 + 1
I2C2->TRISE |= 37;
//开启I2C2使能
I2C2->CR1 |= I2C_CR1_PE;
4.定义起始和结束信号函数
cpp
//发出起始信号
uint8_t I2C_Start(void){
//产生一个起始信号
I2C2->CR1 |= I2C_CR1_START;
//引入超时时间
uint16_t timeout = 0xffff;
//等待起始信号发送
while((I2C2->SR1 & I2C_SR1_SB) == 0 && timeout){
timeout--;
}
return timeout ? 1 : 0;
}
//发出停止信号
void I2C_Stop(void){
I2C2->CR1 |= I2C_CR1_STOP;
}
5.定义主机应答和非应答信号函数
cpp
//应答信号
void I2C_Ack(void){
I2C2->CR1 |= I2C_CR1_ACK;
}
//非应答信号
void I2C_Nack(void){
I2C2-CR1 &= ~I2C_CR1_ACK;
}
6.定义发送设备地址和数据函数
cpp
//发送设备地址,并等待应答
uint8_t I2C_SendAddr(uint8_t addr){
//将地址写入DR
I2C2->DR = addr;
//引入超时时间
uint16_t timeout = 0xffff;
//等待应答
while((I2C2->SR1 & I2C_SR1_ADDR) == 0 && timeout){
timeout--;
}
//访问SR2.清除ADDR标志位
if(timeout > 0){
I2C2->SR2;
}
return timeout ? 1 : 0;
}
//发送数据,并等待应答
uint8_t I2C_Sendbyte(uint8_t byte){
//等待DR为空
uint16_t timeout = 0xffff;
while((I2C2->SR1 & I2C_SR1_TXE) == 0 && timeout){
timeout--;
}
//将数据写入DR中
I2C2->DR = byte;
//等待应答
timeout = 0xffff;
while((I2C2->SR1 & I2C_SR1_BTF) == 0 && timeout){
timeout--;
}
return timeout ? 1 : 0;
}
7.定义接收数据函数
cpp
uint8_t I2C_ReadByte(void){
//等待DR填满
timeout = 0xffff;
while((I2C2->SR1 & I2C_SR1_RXNE) == 0 && timeout){
timeout--;
}
return timeout ? I2C3->DR : 0;
}
五、高级定时器
1.时钟的配置
cpp
RCC->APB2 |= RCC_APB2ENR_IOPAEN;
//开启高级定时器的时钟
RCC->APB2 |= RCC_APB2ENR_TIM1EN;
2.设置GPIO的工作模式
cpp
//位复用推挽输出
GPIOA->CRH |= GPIO_CRH_MODE8;
GPIOA->CRH |= GPIO_CRH_CNF8_1;
GPIOA->CRH &= ~GPIO_CRH_CNF8_0;
3.定时器配置
cpp
//预分频值,其为10000 Hz
TIM1->PSC = 7199;
//重装载值。得到0.5s产生一次溢出
TIM1->ARR = 4999;
//设置计数方向
TIM1->CR1 &= ~TIM_CR1_DIR;
//重复计数
TIM1->RCR = 4;
//输出通道配置
//配置通道的输出模式
TIM1->CCMR1 &= ~TIM_CCMR1-CC1S;
//配置通道具体模式,为PWM1
TIM1->CCMR1 |= TIM_CCMR1_OC1M_2;
TIM1->CCMR1 |= TIM_CCMR1_OC1M_1;
TIM1->CCMR1 &= ~TIM_CCMR1_OC1M_0;
//配置占空比
TIM1->CCR1 = 2500;
//配置极性
TIM1->CCER &= ~TIM_CCER_CC1P;
//手动产生一个更新事件,刷新寄存器的值
TIM1->CR1 |= TIM_CR1_URS;
TIM1->EGR |= TIM_EGR_UG;
//开启使能
TIM1->CCER |= TIM_CCER_CC1E;
//主输出使能
TIM1->BDTR |= TIM_BDTR_MOE;
4.定时器开关
cpp
//定时器开函数
void TIM1_Start(void){
TIM1->CR1 |= TIM_CR1_CEN;
}
//定时器关函数
void TIM1_Stop(void){
TIM1->CR1 &= ~TIM_CR1_CEN;
}
六、DMA存储访问
1.时钟的配置
cpp
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
2.DMA配置
cpp
//配置数据传输方向,存储器到存储器,从外设读
DMA1_Channel1->CCR |= DMA_CCR1_MEM2MEM;
DMA11_Channel1->CCR &= ~DMA_CCR1_DIR;
//配置数据宽度,为8位
DMA1_Channel1->CCR &= ~DMA_CCR1_PSIZE;
DMA1_Channel1->CCR &= ~DMA_CCR1_MSIZE;
//开启地址自增
DMA1_Channel1->CCR |= DMA_CCR1_PINC;
DMA1_Channel1->CCR |= DMA_CCR1_MINC;
//开启数据传输完成中断标志
DMA1_Channel1->CCR |= DMA_CCR1_TCIE;
3.NVIC配置
cpp
NVIC_SetPriorityGrouping(3);
NVIC_SetPriority(DMA1_Channel1_IRQn,2);
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
4.定义数据传输
cpp
void DMA1_Transmit(uint32_t srcAddr,uint32_t destAddr,uint16_t dataLen)
{
//设置外设地址
DMA1_Channel1->CPAR = srcAddr;
//设置存储器地址
DMA1_Channel1->CMAR = destAddr;
//设置传输的数据量
DMA1_Channel1->CNDTR = dataLen;
//开启通道,开始传输数据
DMA1_Channel1->CCR |= DMA_CCR1_EN;
}
七、ADC数模转换
1.时钟的配置
cpp
//开启ADC
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
//开启ADC的分频
RCC->CFGR |= RCC_CFGR_ADCPRE_1;
RCC->CFGR &= ~RCC_CFGR_ADCPRE_0;
2.设置GPIO的工作模式
cpp
//将PC0设置为模拟输入,00 00
GPIOC->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
3.ADC配置
cpp
//使用单通道模式
//配置工作模式:禁用扫描模式
ADC1->CR1 &= ~ADC_CR1_SCAN;
//启用连续转换模式
ADC1->CR2 |= ADC_CR2_CONT;
//数据右对齐(默认)
ADC1->CR2 &= ~ADC_CR2_ALIGN;
//设置通道采样时间,001
ADC1->SMPR1 &= ~ADC_SMPR1_SMP10;
ADC1->SMPR1 |= ADC_SMPR1_SMP10_0;
//规则组通道序列配置
//规则组中的通道个数 L
ADC1->SQR1 &= ~ADC_SQR1_L;
//将通道号10保存到序列中的第一位
//进行清零
ADC1->SQR3 &= ~SQR3_SQ1;
ADC1->SQR3 |= 10 << 0;
//选择软件触发AD转换
//先开启外部触发
ADC1->CR2 |= ADC_CR2_EXTTRIG;
//选择软件触发
ADC1->CR2 |= ADC_CR2_EXTSEL;
4.开启转换
cpp
//上电唤醒
ADC1->CR2 |= ADC_CR2_ADON;
//执行校准
ADC1->CR2 |= ADC_CR2_CAL;
//判断校准完成
while(ADC1->CR2 & ADC_CR2_CAL){}
//启动转换
ADC1->CR2 |= ADC_CR2_SWSTART;
//等待转换完成
while((ADC1->SR & ADC_SR_EOC) == 0){}
5.返回模拟电压值
cpp
//计算电压值
return ADC1->DR * 3.3 / 4095;
八、API通信
1.时钟的配置
cpp
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
2.设置GPIO的工作模式
cpp
//推挽输出 00 11
GPIOC->CRH |= GPIO_CRH_MODE13;
GPIOC->CRH &= ~GPIO_CRH_CNF13;
//复用推挽输出 10 11
GPIOA->CRL |= GPIO_CRL_MODE5;
GPIOA->CRL |= GPIO_CRL_CNF5_1;
GPIOA->CRL &= ~GPIO_CRH_CNF5_0;
GPIOA->CRL |= GPIO_CRL_MODE7;
GPIOA->CRL |= GPIO_CRL_CNF7_1;
GPIOA->CRL &= ~GPIO_CRH_CNF7_0;
//该引脚为MISO,改为浮空输入
GPIOA->CRL &= ~GPIO_CRL_MODE6;
GPIOA->CRL |= GPIO_CRL_CNF6_0;
GPIOA->CRL &= ~GPIO_CRH_CNF6_1;
3.SPI相关配置
cpp
//配置SPI为主模式
SPI1->CR1 |= SPI_CR1_MSTR;
//使用软件控制片选信号,直接拉高,用硬件则为接入3.3V
SPI1->CR1 |= SPI_CR1_SSM;
SPI1->CR1 |= SPI_CR1_SSI;
//配置工作模式0,时钟极性和相位
SPI1->CR1 &= ~SPI_CR1_CPOL;
SPI1->CR1 &= ~SPI_CR1_CPHA;
//配置时钟分频系数,选择001
SPI1->CR1 &= ~SPI_CR1_BR;
SPI1->CR1 |= SPI_CR_BR_0;
//设置数据帧格式
SPI1->CR1 &= ~SPI_CR1_DFF;
//配置高位先行MSB
SPI1->CR1 &= ~SPI_CR1_LSBFIRST;
//SPI模块使能
SPI1->CR1 |= SPI_CR1_SPE;
4.交换一个字节的数据
cpp
//将需要发送的数据写入发送缓冲区
//等待发送缓冲区为空
while((SPI->SR &SPI_SR_TXE) == 0){}
//将数据写入DR中
SPI1->DR = byte;
//读取MISO发来的数据
//等待接收缓冲区为非空
while((SPI1->SR & SPI_SR_RXNE) == 0){}
return SPI1->DR;
九、FSMC控制器
1.时钟的配置
cpp
RCC->AHBENR |= RCC_AHBENR_FSMCEN;
RCC->APB2ENR |= (
RCC_APB2ENR_IOPDEN |
RCC_APB2ENR_IOPEEN |
RCC_APB2ENR_IOPFEN |
RCC_APB2ENR_IOPGEN)
2.设置GPIO的工作模式
cpp
//写一部分
//配置地址线
//引脚模式为复用推挽输出 CNF-10,MODE-11
GPIOF->CRL |= (GPIO_CRL_MODE0 |
GPIO_CRL_MODE1)
GPIOF->CRL &= ~(GPIO_CRL_CNF0_1 |
GPIO_CRL_CNF1_1)
GPIOF->CRL &= ~(GPIO_CRL_CNF0_0 |
GPIO_CRL_CNF1_0)
//配置数据线
//复用推挽输出 CNF-10,MODE-11
GPIOD->CRH |= (GPIO_CRL_MODE14 |
GPIO_CRL_MODE15)
GPIOD->CRH &= ~(GPIO_CRL_CNF14_1 |
GPIO_CRL_CNF14_1)
GPIOD->CRH &= ~(GPIO_CRL_CNF15_0 |
GPIO_CRL_CNF15_0)
//配置控制信号引脚
//配置成复用推挽输出
GPIOD->CRL |= (GPIO_CRL_MODE4 | GPIO_CRL_MODE5);
GPIOD->CRL |= (GPIO_CRL_CNF4_1 | GPIO_CRL_CNF5_1);
GPIOD->CRL &= ~(GPIO_CRL_CNF4_0 | GPIO_CRL_CNF5_0);
3.配置FSMC
cpp
//配置FSMC BCR3
//存储块使能
FSMC_Bank1->BTCR[4] |= FSMC_BCR3_MBKEN;
//时钟存储器类型00
FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MTYP;
//禁止访问闪存
FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_FACCEN;
//数据宽度 -01
FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MWID_1;
FSMC_Bank1->BTCR[4] |= FSMC_BCR3_MWID_0;
//地址数据线不复用,默认复用
FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MUXEN;
//开启写使能
FSMC_Bank1->BTCR[4] |= FSMC_BCR3_WREN;
//配置FSMC BTR3
//地址建立时间
FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_ADDSET;
//数据保存时间
FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_DATAST;
FSMC_Bank1->BTCR[5] |= (71 << 8);
十、LCD显示
须结合FSMC进行
1.启动函数
cpp
void LCD_Iint(void)
{
FSMC_Init();
LCD_Reset();
LCD_BGOn();
//寄存器配置函数直接抄写
LCD_RegConfig();
}
2.复位函数
cpp
GPIOG->ODR &= ~GPIO_ODR_ODR15;
Delay_ms(100);
GPIOG->ODR |= GPIO_ODR_ODR15;
Delay_ms(100);
3.开关背光
cpp
//开启背光
GPIOB->ODR |= GPIO_ODR_ODR0;
//关闭背光
GPIOB->ODR &= ~GPIO_ODR_ODR0;
4.写命令和写数据函数
cpp
//写命令
* LCD_ADDR_CMD = cmd;
//写数据
* LCD_ADDR_DATA = data;