GD32的GD库开发

所有的Cortex-M处理器都有相同的SysTick定时器,因为CMSIS-Core头文件中定义了一个名为SysTick的结构体。

这个定时器可以用作延时函数,不管是STM32的芯片还是GD32,AT32的芯片,delay函数都可以这么写,只要它是cortex-M3/M4的芯片。

以下代码基于GD32F303,主频120M。

延迟函数

示例代码

代码移植于江科大。

/*** @brief  微秒级延时* @param  xus 延时时长,范围:0~233015* @retval 无*/
void Delay_us(uint32_t xus)
{//这里假设主频为72M,如果主频不是72M,自行修改即可SysTick->LOAD = 72 * xus;				//设置定时器重装值SysTick->VAL = 0x00;					//清空当前计数值SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器while(!(SysTick->CTRL & 0x00010000));	//等待计数到0SysTick->CTRL = 0x00000004;				//关闭定时器
}/*** @brief  毫秒级延时* @param  xms 延时时长,范围:0~4294967295* @retval 无*/
void Delay_ms(uint32_t xms)
{while(xms--){Delay_us(1000);}
}/*** @brief  秒级延时* @param  xs 延时时长,范围:0~4294967295* @retval 无*/
void Delay_s(uint32_t xs)
{while(xs--){Delay_ms(1000);}
} 


GPIO

参数配置

和STM32的GPIO类似。

库函数调用

时钟使能:rcu_periph_clock_enable()

GPIO参数初始化:gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin)

以初始化PA8,PD2为例

rcu_periph_clock_enable(RCU_GPIOA);  //GPIOA时钟使能
rcu_periph_clock_enable(RCU_GPIOD);  //GPIOD时钟使能gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8); //设置PA8推挽输出
gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); //设置PD2推挽输出

写引脚电平

gpio_bit_write(GPIOA, GPIO_PIN_8, SET):

读引脚电平

gpio_input_bit_get(GPIOA,GPIO_PIN_13) 

翻转引脚电平

//翻转IO口状态
void gpio_togglepin(uint32_t gpio_periph, uint32_t pin)
{uint32_t octl;octl = GPIO_OCTL(gpio_periph);GPIO_BOP(gpio_periph) = ((octl & pin) << 16u) | (~octl & pin);
}


ADC

参数配置

ADC初始化

//初始化ADC																   
void  Adc_Init(void)
{    rcu_periph_clock_enable(RCU_GPIOC);                             //使能GPIOC时钟gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_4); //AD采集引脚(PC4)模式设置,模拟输入 rcu_periph_clock_enable(RCU_ADC0);   //使能ADC0时钟adc_deinit(ADC0);   //复位ADC0//ADC时钟来自APB2,频率为120Mhz//使用6分频,得到APB2/6 = 20Mhz的ADC时钟rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6);   //配置ADC时钟adc_mode_config(ADC_MODE_FREE);   //ADC独立工作模式              adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE);        //非扫描模式	adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, DISABLE);  //禁止连续模式 adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);             //数据右对齐adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);                               //常规序列使能外部触发adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); //常规序列使用软件触发adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);   //规则序列长度,1个转换在规则序列中,也就是只转换规则序列1 adc_enable(ADC0);   //使能ADC0adc_calibration_enable(ADC0);   //使能ADC0校准复位}

函数调用

获取ADC值

//获得ADC转换后的结果
//ch:通道值 0~17
//返回值:转换结果
uint16_t Get_Adc(uint8_t ch)   
{uint16_t adc_value = 0; adc_regular_channel_config(ADC0, 0, ch, ADC_SAMPLETIME_239POINT5);//配置ADC规则通道组,选择采样时间为239.5周期,提高采样时间可以提高精确度adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);   //软件触发使能,常规序列转换开始while(SET != adc_flag_get(ADC0,ADC_FLAG_EOC));   //等待转换结束	adc_value = adc_regular_data_read(ADC0);         //读ADC规则组数据寄存器return  adc_value;   //返回最近一次ADC0的转换结果
}

获取多组ADC平均值

//获取通道ch的转换值,取times次,然后平均 
//ch:通道编号
//times:获取次数
//返回值:通道ch的times次转换结果平均值
uint16_t Get_Adc_Average(uint8_t ch,uint8_t times)
{uint32_t temp_val=0;uint8_t t;for(t=0;t<times;t++){temp_val+=Get_Adc(ch);  //获取times次数据 delay_ms(5);}return temp_val/times;    //返回平均值
} 

自行转换ADC值为电压

adc_val = Get_Adc_Average(ADC_CH14,20);
adc_volt = (float)adc_val/4096.0f * 3.3f;

串口

参数配置

uart.c。

代码分为三部分。

第一部分为os以及printf函数的相关定义。

第二部分为串口的初始化

第三部分为串口的中断处理函数

#include "usart.h"/* 如果使用os,则包括下面的头文件即可. */
#if SYS_SUPPORT_OS
#include "includes.h" /* os 使用 */
#endif/******************************************************************************************/
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */#if 1#if (__ARMCC_VERSION >= 6010050)            /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t");  /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");    /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)struct __FILE
{int handle;/* Whatever you require here. If the only file you are using is *//* standard output using printf() for debugging, no file handling *//* is required. */
};#endif/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{ch = ch;return ch;
}/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{x = x;
}char *_sys_command_string(char *cmd, int len)
{return NULL;
}/* FILE 在 stdio.h里面定义. */
FILE __stdout;/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{while(RESET == usart_flag_get(USART0, USART_FLAG_TC));       /* 等待上一个字符发送完成 */usart_data_transmit(USART0, (uint8_t)ch);                    /* 将要发送的字符 ch 写入到DR寄存器 */  return ch;
}
#endif
/******************************************************************************************/#if USART_EN_RX /*如果使能了接收*//* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t USART_RX_BUF[USART_REC_LEN];/*  接收状态*  bit15,      接收完成标志*  bit14,      接收到0x0d*  bit13~0,    接收到的有效字节数目
*/
uint16_t USART_RX_STA = 0;//串口0初始化函数
//bound: 波特率, 根据自己需要设置波特率值
void usart_init(uint32_t bound)
{//使能GPIO时钟和复用时钟rcu_periph_clock_enable(RCU_GPIOA);     //使能GPIOA时钟rcu_periph_clock_enable(RCU_AF);        //使能复用时钟rcu_periph_clock_enable(RCU_USART0);    //使能串口时钟//配置TX的GPIOgpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);//配置RX的GPIOgpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);//配置USART的参数usart_deinit(USART0);                                 //RCU配置恢复默认值usart_baudrate_set(USART0, bound);                    //设置波特率usart_stop_bit_set(USART0, USART_STB_1BIT);           //一个停止位usart_word_length_set(USART0, USART_WL_8BIT);         //字长为8位数据格式usart_parity_config(USART0, USART_PM_NONE);           //无奇偶校验位usart_receive_config(USART0, USART_RECEIVE_ENABLE);   //使能接收usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); //使能发送usart_interrupt_enable(USART0, USART_INT_RBNE);       //使能接收缓冲区非空中断//配置NVIC,并设置中断优先级nvic_irq_enable(USART0_IRQn, 3, 3);                   //抢占优先级3,子优先级3//使能串口usart_enable(USART0);	
}void USART0_IRQHandler(void)
{uint8_t Res;
#if SYSTEM_SUPPORT_OS 		                              //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.OSIntEnter();    
#endifif(usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾(回车/换行) ){Res =usart_data_receive(USART0);	//读取接收到的数据if((USART_RX_STA&0x8000)==0)//接收未完成{if(USART_RX_STA&0x4000)//接收到了0x0d{if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始else USART_RX_STA|=0x8000;	//接收完成了 }else //还没收到0X0D{	if(Res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  }		 }}   		 } 
#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.OSIntExit();  											 
#endif
}
#endif

uart.h

#ifndef __USART_H
#define __USART_H#include "stdio.h"
#include "sys.h"
//	 
//串口0初始化
// 
//如果想串口中断接收,请不要注释以下宏定义
#define USART_REC_LEN               200         /* 定义最大接收字节数 200 */
#define USART_EN_RX                 1           /* 使能(1)/禁止(0)串口0接收 */extern uint8_t  USART_RX_BUF[USART_REC_LEN];    /*接收缓冲,最大USART_REC_LEN个字节.末字节为换行符*/ 
extern uint16_t USART_RX_STA;         		      /*接收状态标记*/	void usart_init(uint32_t bound);                /* 串口初始化函数 */#endif

库函数调用

串口使用的代码参考

if(USART_RX_STA&0x8000)    //接收完了一次数据{					    len=USART_RX_STA&0x3fff; //得到此次接收到的数据长度printf("\r\n您发送的消息为:\r\n");for(t=0;t<len;t++){usart_data_transmit(USART0, USART_RX_BUF[t]);         //发送接收到的数据while(RESET == usart_flag_get(USART0, USART_FLAG_TC));//等待发送结束}printf("\r\n\r\n");      //插入换行USART_RX_STA=0;}else{times++;if(times%5000==0){printf("\r\nWKS MiniGD32开发板 串口实验\r\n\r\n");}if(times%200==0)printf("请输入数据,以回车键结束\r\n");  if(times%30==0) LED0_TOGGLE();//闪烁LED,提示系统正在运行.delay_ms(10);   } 


PWM输出

PWM频率计算公式

公式应用:

如果我们要输出1000hz,占空比为50%的方波信号。

假如系统频率为120MHz = 120*10^6Hz。可以这样设置

PSC = 1200 - 1,PSC + 1 = 1200。ARR = 100 - 1。ARR + 1 = 100。

f = fsystem / [ (PSC+1)*(ARR+1) ] = 1000Hz

参数配置

定时器初始化,这里设置上电后默认输出1000Hz,占空比为50%。

/*** @brief 初始化TIMER0为PWM模式** 该函数用于初始化TIMER0定时器为PWM模式,设置相关的时钟、GPIO和定时器参数,* 以生成PWM信号。PWM信号的频率和占空比可以通过参数arr和psc进行调整。** @param arr 自动重装载值,决定了PWM信号的周期* @param psc 预分频值,决定了定时器的时钟频率*/
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
void TIM0_PWM_Init(uint16_t arr,uint16_t psc)
{//定义初始化结构体变量timer_oc_parameter_struct timer_ocinitpara;timer_parameter_struct timer_initpara;rcu_periph_clock_enable(RCU_GPIOA);    //使能GPIOA时钟rcu_periph_clock_enable(RCU_TIMER0);   //使能TIMER0时钟rcu_periph_clock_enable(RCU_AF);       //使能AF时钟gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);  //设置PA8复用推挽输出timer_deinit(TIMER0);                                  //复位TIMER0timer_initpara.prescaler         = psc;                //设置预分频值timer_initpara.alignedmode       = TIMER_COUNTER_EDGE; //设置对齐模式timer_initpara.counterdirection  = TIMER_COUNTER_UP;   //设置向上计数模式timer_initpara.period            = arr;                //设置自动重装载值。/* ARR-1即为周期 */timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;   //设置时钟分频因子timer_initpara.repetitioncounter = 0;                  //设置重复计数值timer_init(TIMER0, &timer_initpara);                   //根据参数初始化定时器timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;                    //使能通道输出timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;               //设置通道输出极性为高timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocinitpara);  //定时器通道输出配置 timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, arr/2);               //设置占空比,这里默认设置比较值为自动重装载值的一半,即占空比为50%timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0);         //设置通道比较模式为PWM模式0 timer_primary_output_config (TIMER0, ENABLE);                                     //TIMER0所有的通道输出使能timer_enable(TIMER0);                                                             //使能定时器TIMER0
}

//设置TIMER0通道0的占空比
//compare:比较值 CCR
void TIM_SetTIM0Compare1(uint32_t compare)
{/* compare的取值范围:0-ARR。占空比 = CCR/(ARR+1) */timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, compare);
}

库函数调用

初始化,并填入ARR和PSC参数。

TIM0_PWM_Init(100-1,1200-1);               //默认设置1000HZ,占空比为50%


PWM捕获

参数配置

库函数调用


定时器

参数配置

定时器初始化配置

//通用定时器2中断初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//APB1时钟为60MHz,TIMER2时钟选择为APB1的2倍,因此,TIMER2时钟为120MHz
void TIM2_Int_Init(uint16_t arr,uint16_t psc)
{timer_parameter_struct timer_initpara;               //timer_initpara用于存放定时器的参数//使能RCU相关时钟 rcu_periph_clock_enable(RCU_TIMER2);                 //使能TIMER2的时钟//复位TIMER2timer_deinit(TIMER2);                                //复位TIMER2timer_struct_para_init(&timer_initpara);             //初始化timer_initpara为默认值//配置TIMER2timer_initpara.prescaler         = psc;              //设置预分频值timer_initpara.counterdirection  = TIMER_COUNTER_UP; //设置向上计数模式timer_initpara.period            = arr;              //设置自动重装载值timer_initpara.clockdivision     = TIMER_CKDIV_DIV1; //设置时钟分频因子timer_init(TIMER2, &timer_initpara);                 //根据参数初始化定时器//使能定时器及其中断timer_interrupt_enable(TIMER2, TIMER_INT_UP);        //使能定时器的更新中断nvic_irq_enable(TIMER2_IRQn, 1, 3);                  //配置NVIC设置优先级,抢占优先级1,响应优先级3timer_enable(TIMER2);                                //使能定时器TIMER2
}

函数调用

//定时器2中断服务程序
void TIMER2_IRQHandler(void)
{if(timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP) == SET)   //判断定时器更新中断是否发生{/*--功能代码--*/LED1_TOGGLE(); //LED1翻转/*-----------*/timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);         //清除定时器更新中断标志}
}

IIC

底层驱动

.c文件

//初始化IIC
void IIC_Init(void)
{rcu_periph_clock_enable(RCU_GPIO_IIC);  //GPIOC时钟使能//SCL引脚模式设置,推挽输出 gpio_init(GPIO_SCL, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_SCL);//SDA引脚模式设置,开漏输出,这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平gpio_init(GPIO_SDA, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_SDA);IIC_Stop();     //停止总线上所有设备   
}//IIC延时函数,用于控制IIC读写速度
static void IIC_delay(void)
{delay_us(2);    //2us的延时
}//产生IIC起始信号
void IIC_Start(void)
{IIC_SDA(1);IIC_SCL(1);IIC_delay();IIC_SDA(0);     //START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 IIC_delay();IIC_SCL(0);     //钳住I2C总线,准备发送或接收数据 IIC_delay();
}	  //产生IIC停止信号
void IIC_Stop(void)
{IIC_SDA(0);     //STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号IIC_delay();IIC_SCL(1);IIC_delay();IIC_SDA(1);     //发送I2C总线结束信号 IIC_delay();				   	
}//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{uint8_t waittime = 0;uint8_t rack = 0;IIC_SDA(1);       //主机释放SDA线(此时外部器件可以拉低SDA线)IIC_delay();	    IIC_SCL(1);       //SCL=1, 此时从机可以返回ACK    IIC_delay();	    while(IIC_READ_SDA)       //等待应答 {waittime++;if(waittime>250){IIC_Stop();rack = 1;break;      //没有收到应答信号}}IIC_SCL(0);     //SCL=0, 结束ACK检查  IIC_delay();	return rack;  
} //产生ACK应答
void IIC_Ack(void)
{IIC_SDA(0);     //SCL 0 -> 1  时 SDA = 0,表示应答 IIC_delay();IIC_SCL(1);     //产生一个时钟 IIC_delay();IIC_SCL(0);IIC_delay();IIC_SDA(1);     //主机释放SDA线 IIC_delay();
}//不产生ACK应答		    
void IIC_NAck(void)
{IIC_SDA(1);     //SCL 0 -> 1  时 SDA = 1,表示不应答 IIC_delay();IIC_SCL(1);     //产生一个时钟 IIC_delay();IIC_SCL(0);IIC_delay();
}		//IIC发送一个字节
//data: 要发送的数据	  
void IIC_Send_Byte(uint8_t data)
{                        uint8_t t;   for(t=0;t<8;t++){              IIC_SDA((data & 0x80) >> 7);    //高位先发送 IIC_delay();IIC_SCL(1);IIC_delay();IIC_SCL(0);data <<= 1;      //左移1位,用于下一位发送 }	 IIC_SDA(1);         //发送完成, 主机释放SDA线 
} 	    //IIC读取一个字节
//ack:ack=1时,发送ACK,ack=0,发送nACK   
//返回值:接收到的数据
uint8_t IIC_Read_Byte(uint8_t ack)
{uint8_t i,receive=0;for(i=0;i<8;i++ )     //接收1个字节数据 {receive <<= 1;    //高位先输出,所以先收到的数据位要左移IIC_SCL(1);IIC_delay();if (IIC_READ_SDA){receive++;}IIC_SCL(0);IIC_delay();}					 if (!ack)IIC_NAck();       //发送nACKelseIIC_Ack();        //发送ACK   return receive;       //返回读到的数据
}

.h

#ifndef _MYIIC_H
#define _MYIIC_H
#include "sys.h"/* 移植时只需修改引脚相关宏定义 */
#define RCU_GPIO_IIC             RCU_GPIOC#define GPIO_SCL            GPIOC
#define GPIO_PIN_SCL        GPIO_PIN_12#define GPIO_SDA            GPIOC
#define GPIO_PIN_SDA        GPIO_PIN_11
/*----------------------------*/
//IO操作
#define IIC_SCL(x)          gpio_bit_write(GPIO_SCL, GPIO_PIN_SCL,(bit_status)(x))  //SCL
#define IIC_SDA(x)          gpio_bit_write(GPIO_SDA, GPIO_PIN_SDA, (bit_status)(x))  //SDA
#define IIC_READ_SDA        gpio_input_bit_get(GPIO_SDA,GPIO_PIN_SDA)  //读取SDA//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);				        //发送IIC开始信号
void IIC_Stop(void);	  			      //发送IIC停止信号
void IIC_Ack(void);					        //IIC发送ACK信号
void IIC_NAck(void);				        //IIC不发送ACK信号
uint8_t IIC_Wait_Ack(void); 				//IIC等待ACK信号
void IIC_Send_Byte(uint8_t data);		//IIC发送一个字节
uint8_t IIC_Read_Byte(uint8_t ack); //IIC读取一个字节#endif


SPI

底层驱动

.c头文件

//SPI初始化代码,配置成主机模式
//这里是针对SPI0的初始化
void SPI0_Init(void)
{spi_parameter_struct spi_init_struct;rcu_periph_clock_enable(RCU_GPIOA);    //GPIOA时钟使能rcu_periph_clock_enable(RCU_SPI0);     //SPI0时钟使能rcu_periph_clock_enable(RCU_AF);       //复用时钟使能//PA5,6,7复用推挽输出gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);  spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;   //全双工模式spi_init_struct.device_mode          = SPI_MASTER;                 //主机模式spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;         //8位数据帧格式 spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;    //空闲状态下,CLK保持高电平,在第二个时钟跳变沿采集第一个数据spi_init_struct.nss                  = SPI_NSS_SOFT;               //NSS软件模式,NSS电平取决于SWNSS位spi_init_struct.prescale             = SPI_PSC_256;                //默认使用256分频, 速度最低spi_init_struct.endian               = SPI_ENDIAN_MSB;             //MSB先传输spi_init(SPI0, &spi_init_struct);spi_enable(SPI0);	                                                 //使能SPI0	
}//SPI0速度设置函数
//SPI0时钟选择来自APB2, 即PCLK2, 为120Mhz
//SPI速度 = PCLK2 / 2^(speed + 1)
//speed   : SPI0时钟分频系数
void SPI0_SetSpeed(uint8_t speed)
{speed &= 0X07;                  //限制范围spi_disable(SPI0);	            //SPI0失能SPI_CTL0(SPI0) &= ~(7 << 3);    //先清零SPI_CTL0(SPI0) |= speed << 3;   //设置分频系数spi_enable(SPI0);	              //SPI0使能
}//SPI0读写一个字节数据
//txdata  : 要发送的数据(1字节)
//返回值:接收到的数据(1字节)
uint8_t SPI0_ReadWriteByte(uint8_t txdata)
{   while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE));    //等待发送缓冲区空spi_i2s_data_transmit(SPI0, txdata);                     //发送一个字节while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE));   //等待接收缓冲区非空return spi_i2s_data_receive(SPI0);                       //返回收到的数据
}

.h头文件

#ifndef __SPI_H
#define __SPI_H#include "sys.h"//SPI总线速度设置 
#define SPI_SPEED_2         0
#define SPI_SPEED_4         1
#define SPI_SPEED_8         2
#define SPI_SPEED_16        3
#define SPI_SPEED_32        4
#define SPI_SPEED_64        5
#define SPI_SPEED_128       6
#define SPI_SPEED_256       7void SPI0_Init(void);                           //SPI0初始化               
void SPI0_SetSpeed(uint8_t speed);              //设置SPI0速度
uint8_t SPI0_ReadWriteByte(uint8_t txdata);     //SPI0读写一个字节#endif


Flash读写

函数调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/8958.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

跨域问题及解决方案

跨域问题不仅影响开发效率&#xff0c;还可能导致项目进度延误。因此&#xff0c;理解和掌握跨域问题的原理及其解决方案对于前端开发者和后端开发者来说都至关重要。本文将详细介绍什么是跨域、跨域产生的原因&#xff0c;以及常见的后端跨域解决方案。 文章目录 一、什么是跨…

MoE的学习

1.MoE的介绍 混合专家模型&#xff08;Mixture of Experts&#xff0c;MoE&#xff09;是一种先进的神经网络架构&#xff0c;旨在通过整合多个模型或“专家”的预测来提升整体模型性能。MoE模型的核心思想是将输入数据分配给不同的专家子模型&#xff0c;然后将所有子模型的输…

c++学习第十四天

提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考。 //力扣代码 class Solution {const char* numStrArr[10]{"","","abc","def","ghi","jkl","mno","pqrs","tuv&q…

【deepseek】deepseek-r1本地部署-第二步:huggingface.co替换为hf-mirror.com国内镜像

一、背景 由于国际镜像国内无法直接访问&#xff0c;会导致搜索模型时加载失败&#xff0c;如下&#xff1a; 因此需将国际地址替换为国内镜像地址。 二、操作 1、使用vscode打开下载路径 2、全局地址替换 关键字 huggingface.co 替换为 hf-mirror.com 注意&#xff1a;务…

循序渐进kubernetes-RBAC(Role-Based Access Control)

文章目录 概要Kubernetes API了解 Kubernetes 中的 RBACRoles and Role Bindings:ClusterRoles and ClusterRoleBindings检查访问权限&#xff1a;外部用户结论 概要 Kubernetes 是容器化应用的强大引擎&#xff0c;但仅仅关注部署和扩展远远不够&#xff0c;集群的安全同样至…

思维练习题

目录 第一章 假设法1.题目1. 如何问问题2. 他们的职业是分别什么3. 谁做对了4. 鞋子的颜色 2.答案 空闲时间写一些思维题来锻炼下思维逻辑&#xff08;题目均收集自网上&#xff0c;分析推理为自己所写&#xff09;。 第一章 假设法 一个真实的假设往往可以让事实呈现眼前&…

HarmonyOS:创建应用静态快捷方式

一、前言 静态快捷方式是一种在系统中创建的可以快速访问应用程序或特定功能的链接。它通常可以在长按应用图标&#xff0c;以图标和相应的文字出现在应用图标的上方&#xff0c;用户可以迅速启动对应应用程序的组件。使用快捷方式&#xff0c;可以提高效率&#xff0c;节省了查…

深入探索C++17的std::any:类型擦除与泛型编程的利器

文章目录 基本概念构建方式构造函数直接赋值std::make_anystd::in_place_type 访问值值转换引用转换指针转换 修改器emplaceresetswap 观察器has_valuetype 使用场景动态类型的API设计类型安全的容器简化类型擦除实现 性能考虑动态内存分配类型转换和异常处理 总结 在C17的标准…

DeepSeek-R1 蒸馏模型及如何用 Ollama 在本地运行DeepSeek-R1

在人工智能飞速发展的领域中&#xff0c;大型语言模型&#xff08;LLMs&#xff09;的出现可谓是一项重大变革。在这些模型里&#xff0c;DeepSeek - R1 及其蒸馏模型备受瞩目&#xff0c;它们融合了独特的能力与高可用性。今天我们一起聊一下 DeepSeek - R1 蒸馏模型究竟是什么…

机器学习day3

自定义数据集使用框架的线性回归方法对其进行拟合 import matplotlib.pyplot as plt import torch import numpy as np # 1.散点输入 # 1、散点输入 # 定义输入数据 data [[-0.5, 7.7], [1.8, 98.5], [0.9, 57.8], [0.4, 39.2], [-1.4, -15.7], [-1.4, -37.3], [-1.8, -49.1]…

java多线程学习笔记

文章目录 关键词1.什么是多线程以及使用场景?2.并发与并行3.多线程实现3.1继承 Thread 类实现3.2Runnable 接口方式实现3.3Callable接口/Future接口实现3.4三种方式总结 4.常见的成员方法&#xff08;重点记忆&#xff09;94.1setName/currentThread/sleep要点4.2线程的优先级…

无耳科技 Solon v3.0.7 发布(2025农历新年版)

Solon 框架&#xff01; Solon 框架由杭州无耳科技有限公司&#xff08;下属 Noear 团队&#xff09;开发并开源。是新一代&#xff0c;面向全场景的 Java 企业级应用开发框架。从零开始构建&#xff08;非 java-ee 架构&#xff09;&#xff0c;有灵活的接口规范与开放生态。…

Redis常用命令合集【一】

1.Redis常用命令 Redis是典型的key-value数据库&#xff0c;key一般是字符串&#xff0c;而value包含很多不同的数据类型&#xff1a; Redis为了方便我们学习&#xff0c;将操作不同数据类型的命令也做了分组&#xff0c;在官网&#xff08; https://redis.io/commands &#…

python学opencv|读取图像(四十八)使用cv2.bitwise_xor()函数实现图像按位异或运算

【0】基础定义 按位与运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;全1取1&#xff0c;其余取0。 按位或运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;有1取1&#xff0c;其余取0。 按位取反运算&#xff1a;一个二进制数&#xff0c;0变1,1变0。 按…

docker 学习笔记

一、docker容器快速上手以及简单操作 docker的image和container image镜像 docker image就是一个read.only文件&#xff0c;可以理解成一个模版&#xff0c;docker image具有分层的概念 可以自己制作&#xff0c;也可以从registry拉去 container容器 一个运行中的docker …

【PyTorch】5.张量索引操作

目录 1. 简单行、列索引 2. 列表索引 3. 范围索引 4. 布尔索引 5. 多维索引 个人主页&#xff1a;Icomi 在深度学习蓬勃发展的当下&#xff0c;PyTorch 是不可或缺的工具。它作为强大的深度学习框架&#xff0c;为构建和训练神经网络提供了高效且灵活的平台。神经网络作为…

穿心莲内酯(andrographolide)生物合成CYP72-文献精读106

Two CYP72 enzymes function as Ent-labdane hydroxylases in the biosynthesis of andrographolide in Andrographis paniculata 两种CYP72酶在穿心莲&#xff08;Andrographis paniculata&#xff09;中作为Ent-labdane羟化酶&#xff0c;在穿心莲内酯&#xff08;andrograp…

关于圆周率的新认知 - 2

当未知长度的单位 1 和已完成长度的单位 1 之间的比例不是 1:1 而是其它的数值的时候&#xff0c;不难看出&#xff0c;这时候的圆周率就变成了“椭圆周率”。你可能要说&#xff0c;这不是椭圆积分吗&#xff1f;对了&#xff0c;这就是椭圆积分。但是我们不要考虑什么椭圆积分…

ARM64平台Flutter环境搭建

ARM64平台Flutter环境搭建 Flutter简介问题背景搭建步骤1. 安装ARM64 Android Studio2. 安装Oracle的JDK3. 安装 Dart和 Flutter 开发插件4. 安装 Android SDK5. 安装 Flutter SDK6. 同意 Android 条款7. 运行 Flutter 示例项目8. 修正 aapt2 报错9. 修正 CMake 报错10. 修正 N…

进程池的制作(linux进程间通信,匿名管道... ...)

目录 一、进程间通信的理解 1.为什么进程间要通信 2.如何进行通信 二、匿名管道 1.管道的理解 2.匿名管道的使用 3.管道的五种特性 4.管道的四种通信情况 5.管道缓冲区容量 三、进程池 1.进程池的理解 2.进程池的制作 四、源码 1.ProcessPool.hpp 2.Task.hpp 3…