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读写
函数调用
相关文章:
GD32的GD库开发
所有的Cortex-M处理器都有相同的SysTick定时器,因为CMSIS-Core头文件中定义了一个名为SysTick的结构体。 这个定时器可以用作延时函数,不管是STM32的芯片还是GD32,AT32的芯片,delay函数都可以这么写,只要它是cortex-M…...
DeepSeek R1:推理模型新纪元与价格战
标题:DeepSeek R1:推理模型新纪元与价格战 文章信息摘要: DeepSeek R1的发布标志着推理模型研究的重要转折点,其采用四阶段强化学习训练方法,结合监督微调和拒绝采样,显著提升了模型的推理能力。这一进展不…...
一文简单回顾Java中的String、StringBuilder、StringBuffer
简单说下String、StringBuilder、StringBuffer的区别 String、StringBuffer、StringBuilder在Java中都是用于处理字符串的,它们之间的区别是String是不可变的,平常开发用的最多,当遇到大量字符串连接的时候,就用StringBuilder&am…...
机器学习:支持向量机
支持向量机(Support Vector Machine)是一种二类分类模型,其基本模型定义为特征空间上的间隔最大的广义线性分类器,其学习策略便是间隔最大化,最终可转化为一个凸二次规划问题的求解。 假设两类数据可以被 H x : w T x…...
简单的停车场管理系统的C语言实现示例
以下是一个简单的停车场管理系统的C语言实现示例。该示例使用结构体来管理停车场的车位信息,并提供基本车辆进入、离开以及显示停车场状态功能。 #include <stdio.h> #include <stdlib.h> #include <string.h>#define MAX_SLOTS 10 // 最大车位数…...
网络工程师 (3)指令系统基础
一、寻址方式 (一)指令寻址 顺序寻址:通过程序计数器(PC)加1,自动形成下一条指令的地址。这是计算机中最基本、最常用的寻址方式。 跳跃寻址:通过转移类指令直接或间接给出下一条指令的地址。跳…...
基于Python的智慧物业管理系统
【Python】基于Python的智慧物业管理系统(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目背景二、研究目的三、项目意义四、项目功能五、项目创新点六、开发技术介绍七、项目界面展示(部分展示,详细看视频)八、项…...
使用 Vue 3 的 watchEffect 和 watch 进行响应式监视
Vue 3 的 Composition API 引入了 <script setup> 语法,这是一种更简洁、更直观的方式来编写组件逻辑。结合 watchEffect 和 watch,我们可以轻松地监视响应式数据的变化。本文将介绍如何使用 <script setup> 语法结合 watchEffect 和 watch&…...
环境变量
目录 一.概念介绍 1.1命令行参数 二.一个例子,一个环境变量 2.1查看环境变量 2.2如何理解环境变量呢?存储的角度 2.3环境变量最开始从哪里来的呢? 概括: 1. 环境变量的存储 2. 命令查找过程 3. 环境变量表和命令行参数…...
Scale AI 创始人兼 CEO采访
Scale AI 创始人兼 CEO 亚历山大王(Alexander Wang)首次亮相节目接受采访。他的公司专注于为人工智能工具提供准确标注的数据。早在 2022 年,王成为世界上最年轻的白手起家亿万富翁。 美国在全球人工智能竞赛中的地位,以及它与中…...
MongoDB中常用的几种高可用技术方案及优缺点
MongoDB 的高可用性方案主要依赖于其内置的 副本集 (Replica Set) 和 Sharding 机制。下面是一些常见的高可用性技术方案: 1. 副本集 (Replica Set) 副本集是 MongoDB 提供的主要高可用性解决方案,确保数据在多个节点之间的冗余存储和自动故障恢复。副…...
【Erdas实验教程】001:Erdas2022下载及安装教程
文章目录 一、Erdas2022安装教程1. 安装主程序2. 拷贝补丁3. 安装LicenseServer4. 运行软件 二、Erdas2022下载地址 一、Erdas2022安装教程 Erdas2022全新界面如下: 1. 安装主程序 下载安装包并解压,以管理员身份运行 “setup.exe” 或 “setup.vbs”&…...
Python3 【函数】水平考试:精选试题和答案
Python3 【函数】水平考试:精选试题和答案 Python 函数考试试卷及答案。共计30题,其中选择题15题、填空题10题、编程题5题,试卷满分为100分。 一、选择题(每题 2 分,共 30 分) 以下哪个关键字用于定义函数…...
stm8s单片机(三)时钟系统与时钟切换
一个单片机系统要正常运行应包括四个部分: 电源,晶振,复位电路,下载电路。 晶振就是时钟。 stm8有四种时钟源 HSE (High Speed External clock signal)HSE user-ext (High Speed External clock signal user external)HSI (High Speed Inter…...
ChatGPT高效处理图片技巧使用详解
ChatGPT,作为OpenAI开发的预训练语言模型,主要用于生成自然语言文本的任务。然而,通过一些技巧和策略,我们可以将ChatGPT与图像处理模型结合,实现一定程度上的图像优化和处理。本文将详细介绍如何使用ChatGPT高效处理图…...
图漾Halcon版本SDK使用教程【V1.1.0新版本】
文章目录 1.下载并安装 Halcon1.1 下载Halcon软件1.2 安装Halcon 2.下载Camport_Halcon_gentl SDK2.1 下载Camport_Halcon_gentl SDK2.2 Camport Halcon SDK介绍2.3 Halcon SDK环境配置与运行2.3.1 SDK环境配置2.3.2 获取相机支持的参数2.3.3 配置相机参数并运行相机 2.4 遍历H…...
C语言二级
//请编写函数fun(),该函数的功能是:计算并输出给定整数n的所有因 //子(不包括1和自身)之和。规定n的值不大于1000。例如,在主函数 //中从键盘给n输入的值为856,则输出为:sum 763。 //注意&…...
软工_软件工程
2025.01.24:软件工程导论学习笔记 第2节 软件工程 2.1 软件发展 - 四个阶段2.1.1 程序设计2.1.2 程序系统2.1.3 软件工程2.1.4 第四阶段 2.2 软件危机2.2.1 软件危机 - 定义2.2.2 软件危机 - 主要表现 2.1 软件发展 - 四个阶段 2.1.1 程序设计 软件生产个体化&…...
【creo】CREO配置快捷键方式和默认单位
了解CREO工作目录设置 设置快捷方式启动目录,就能自动加载其中的配置。 一、通过键盘快捷方式 保存配置 creo_parametric_customization.ui 文件: 二、通过映射键录制 通过这种方式可以监听鼠标的点击事件。使用键盘快捷方式无法找到需要的动作时候可…...
go理论知识——Go Channel 笔记 [特殊字符]
go理论知识——Go Channel 笔记 📝 1. 基本概念 🧠 1.1 Channel 是什么? Channel 是 Go 语言中用于在不同 Goroutine 之间进行通信的机制。Channel 是类型安全的,意味着你只能发送和接收特定类型的数据。 1.2 Channel 的创建 …...
微信小程序压缩图片
由于wx.compressImage(Object object) iOS 仅支持压缩 JPG 格式图片。所以我们需要做一下特殊的处理: 1.获取文件,判断文件是否大于设定的大小 2.如果大于则使用canvas进行绘制,并生成新的图片路径 3.上传图片 async chooseImage() {let …...
第05章 11 动量剖面可视化代码一则
在计算流体力学(CFD)中,动量剖面(Momentum Profiles)通常用于描述流体在流动方向上的动量分布。在 VTK 中,可以通过读取速度场数据,并计算和展示动量剖面来可视化呈现速度场信息。 示例代码 以…...
Android Studio 新版本24.2.2 运行后自动切到 LogCat
最近更新了 Android studio 版本,发现有个问题: 每次 Run app 之后。都会自动切换到 run 标签。这让我非常不习惯。我个人习惯在app 运行后查看Logcat 最后靠 deepSeek 找到一种解决方案: Android Studio 中截图如下:...
使用EVE-NG-锐捷实现OSPF
一、OSPF基础知识 Open shortest Path First(OSPF)开放式最短路径优先协议 1.OSPF的关系状态 (1)邻居关系(TWO-WAY) 只发送hello包不发送LSA包(链路状态通告包) (2)邻接关系(FULL) OSPF设备与设备之间相互建立OSPF关系,初始为邻居关系(TWO-WAY)状态࿰…...
解决使用Selenium时ChromeDriver版本不匹配问题
在学习Python爬虫过程中如果使用Selenium的时候遇到报错如下session not created: This version of ChromeDriver only supports Chrome version 99… 这说明当前你的chrome驱动版本和浏览器版本不匹配。 例如 SessionNotCreatedException: Message: session not created: This…...
景联文科技加入AIIA联盟数据标注分委会
2025年1月16日,中国人工智能产业发展联盟(简称AIIA)数据委员会数据标注分委会(以下简称“分委会”)正式成立。景联文科技成为第一批AIIA联盟数据标注分委会委员单位。 数据标注分委会的成立旨在搭建数据标注领域产学研…...
IoTDB结合Mybatis使用示例(增删查改自定义sql等)
IoTDB时序库是当前越来越流行以及基于其优势各大厂商越来越易接受的国产开源时序数据库,针对IoTDB的内容不做过多介绍,在使用该时序库时,往往有一定入门门槛,不同于关系型数据库或文档型数据库那般方便维护和接入开发,…...
穷举vs暴搜vs深搜vs回溯vs剪枝系列一>解数独
题目: 解析: 部分决策树: 代码设计&剪枝&回溯: 代码: class Solution {private boolean[][] row, col;private boolean[][][] gird; public void solveSudoku(char[][] board) {//下标->数字ÿ…...
C#@符号在string.Format方法中作用
本文详解@符号在string.Format方法中作用。...
mysql 学习2 MYSQL数据模型,mysql内部可以创建多个数据库,一个数据库中有多个表;表是真正放数据的地方,关系型数据库 。
在第一章中安装 ,启动mysql80 服务后,连接上了mysql,那么就要 使用 SQL语句来 操作mysql数据库了。那么在学习 SQL语言操作 mysql 数据库 之前,要对于 mysql数据模型有一个了解。 MYSQL数据模型 在下图中 客户端 将 SQL语言&…...
JVM栈溢出线上环境排查
#查看当前Linux系统进程ID、线程ID、CPU占用率(-eo后面跟想要展示的列) ps H -eo pid,tid,%cpups H -eo pid,tid,%cpu |grep tid #使用java jstack 查看进程id下所有线程id的情况 jstack pid 案例2 通过jstack 排查死锁问题 #启动java代码 jstack 进…...
【vue3组件】【大文件上传】【断点续传】支持文件分块上传,能够在上传过程中暂停、继续上传的组件
一、概述 本示例实现了一个基于 Vue3 和 TypeScript 的断点上传功能。该功能支持文件分块上传,能够在上传过程中暂停、继续上传,并且支持检测已经上传的分块,避免重复上传,提升上传效率。以下是关键的技术点与实现流程࿱…...
Kafka运维宝典 (三)- Kafka 最大连接数超出限制问题、连接超时问题、消费者消费时间超过限制问题详细介绍
Kafka运维宝典 (三) 文章目录 Kafka运维宝典 (三)一、Kafka Broker 配置中的最大连接数超出限制问题1. 错误原因2. 相关 Kafka 配置参数2.1 connections.max2.2 max.connections.per.ip2.3 num.network.threads2.4 connections.ma…...
【135. 分发糖果 困难】
题目: n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求,给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果,计…...
AAAI2024论文解读|HGPROMPT Bridging Homogeneous and Heterogeneous Graphs
论文标题 HGPROMPT: Bridging Homogeneous and Heterogeneous Graphs for Few-shot Prompt Learning 跨同构异构图的小样本提示学习 论文链接 HGPROMPT: Bridging Homogeneous and Heterogeneous Graphs for Few-shot Prompt Learning论文下载 论文作者 Xingtong Yu, Yuan…...
算法题(49):反转链表II
审题: 需要我们对指定范围的链表进行反转,并返回反转后链表的头结点 思路: 方法一:vector法 我们先遍历一次链表,并把数据对应的存在数组中,然后利用数组的reverse方法进行反转数据,最后再遍历一…...
代码随想录day20
235. 利用二叉搜索树的特性即可 /** lc appleetcode.cn id235 langcpp** [235] 二叉搜索树的最近公共祖先*/// lc codestart /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) :…...
文档智能扫描,提升无纸化办公效率
随着无纸化办公的推广和移动设备的普及,用户迫切需要将纸质文档快速、准确地转换成电子格式,以提高工作效率和信息管理的便捷性。同时,用户将文档扫描成电子版后,可以自行通过加密和访问控制提高电子文档的安全性,以满…...
差分等长的原理
差分等长是指在设计差分信号传输线路时,保证两条差分线的长度尽量一致,长度之差在一个合理的范围内。这是为了确保两个差分信号时刻保持相反极性,减少共模分量,从而提高信号传输的质量。 在差分信号传输中,两条差分线…...
“““【运用 R 语言里的“predict”函数针对 Cox 模型展开新数据的预测以及推理。】“““
主题与背景 本文主要介绍了如何在R语言中使用predict函数对已拟合的Cox比例风险模型进行新数据的预测和推理。Cox模型是一种常用的生存分析方法,用于评估多个因素对事件发生时间的影响。文章通过具体的代码示例展示了如何使用predict函数的不同参数来获取生存概率和…...
真正理解std::move
std::move的作用只有一个,那就是把一个左值强制转换为右值,有了这个右值,右值允许的任何操作就可以实施了。比如:1. 这个右值可以赋给一个左值变量,2. 这个右值可以被一个右值引用来引用。 class A {public:A(int n):…...
Vue3.5 企业级管理系统实战(三):页面布局及样式处理 (Scss UnoCSS )
本章主要是关于整体页面布局及样式处理,在进行这一章代码前,先将前两章中的示例代码部分删除(如Home.vue、About.vue、counter.ts、App.vue中引用等) 1 整体页面布局 页面整体布局构成了产品的框架基础,通常涵盖主导…...
Python3 【函数】项目实战:5 个新颖的学习案例
Python3 【函数】项目实战:5 个新颖的学习案例 本文包含5编程学习案例,具体项目如下: 简易聊天机器人待办事项提醒器密码生成器简易文本分析工具简易文件加密解密工具 项目 1:简易聊天机器人 功能描述: 实现一个简易…...
17.Word:李楠-学术期刊❗【29】
目录 题目 NO1.2.3.4.5 NO6.7.8 NO9.10.11 NO12.13.14.15 NO16 题目 NO1.2.3.4.5 另存为手动/F12Fn光标来到开头位置处→插入→封面→选择花丝→根据样例图片,对应位置填入对应文字 (手动调整即可)复制样式:开始→样式对话框→管理…...
基于GS(Gaussian Splatting)的机器人Sim2Real2Sim仿真平台
项目地址:RoboGSim 背景简介 已有的数据采集方法中,遥操作(下左)是数据质量高,但采集成本高、效率低下;传统仿真流程成本低(下右),但真实度(如纹理、物理&…...
基于Django的豆瓣影视剧推荐系统的设计与实现
【Django】基于Django的豆瓣影视剧推荐系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用了Python作为后端开发语言,采用Django作为后端架构,结…...
提升企业内部协作的在线知识库架构与实施策略
内容概要 在当前快速变化的商业环境中,企业对于提升内部协作效率的需求愈显迫切。在线知识库作为信息存储与共享的平台,成为了推动企业数字化转型的重要工具。本文将深入探讨如何有效打造与实施在线知识库,强调架构设计、知识资产分类管理及…...
Vuex中的getter和mutation有什么区别
在现代前端开发中,状态管理是一个不可忽视的话题,而Vuex作为Vue.js的官方状态管理库,在大型应用中扮演着至关重要的角色。当我们使用Vuex进行状态管理时,getter和mutation是两个重要的概念。虽然它们都是用来处理状态的࿰…...
springboot 动态线程池
在Spring Boot中,可以使用ThreadPoolTaskExecutor类来创建动态线程池。以下是一个示例: 首先,需要在配置文件中配置线程池的属性,例如最小线程数、最大线程数、线程存活时间等。可以在application.properties或application.yml中…...
Android - 通过Logcat Manager简单获取Android手机的Log
由于工作需要,经常需要获取Android手机的Log。 平常都是通过adb命令来获取,每次都要写命令。 偶然的一个机会,我从外网发现了一个工具 Logcat Manager,只需要通过简单的双击即可获取Android的Log,这里也分享一下。 目…...