Le port série STM32(8)-DMA+ réalise la transmission et la réception de données de la carte de développement double

J'ai enregistré ma compréhension du DMA ci-dessous en étudiant la vidéo de l'Université de Jiangke et le blog d'un grand gars du CSDN.

1. Mémoire, registres

Pour la mémoire, j'ai écrit sur la ROM et la RAM dans l'article précédent, en particulier sur la SRAM, la mémoire flash et les registres périphériques. Ici, Jiang Keda a mentionné un point de connaissance, qui est utile pour la compréhension.
D'abord, par exemple, définissez un nombre hexadécimal :
uint8_t a=0x66 ;
puis après compilation, un espace d'adressage sera ouvert en SRAM pour a, par exemple, l'adresse est 0x20000000 correspondant à a, car a est une variable, donc il est SRAM.
Si, avec le mot clé : const, la variable devient une constante,
const uint8_t a=0x66 ;
alors : maintenant a est stocké dans le flash, et l'adresse peut être 0x080000FF. Étant donné que le stockage flash est en lecture seule, la constante ne peut pas être modifiée, il s'agit donc d'un attribut en lecture seule. Par conséquent, pour certaines polices ou tables de recherche, const peut être ajouté pour les stocker en flash afin de libérer de l'espace SRAM.

De plus, si vous souhaitez interroger l'adresse du registre périphérique, recherchez d'abord l'image mémoire dans la fiche technique, recherchez l'adresse de départ de USART2, puis recherchez l'image de registre spécifique dans le chapitre USART2. offset, l'adresse de départ + Offset = adresse de registre spécifique. Si vous voulez le trouver à partir du code :
Par exemple : je veux trouver l'adresse du registre de données DATAR de USART3,
insérez la description de l'image ici
alors je dois trouver l'adresse de départ et le décalage de USART2. Comme indiqué dans la figure ci-dessous :
insérez la description de l'image ici
insérez la description de l'image ici
l'adresse de base de l'USART3 est l'adresse de base des périphériques APB1 + le décalage de 0 x 4800. Vous
insérez la description de l'image ici
pouvez également voir les adresses de base des périphériques flash, SRAM et bus, indiquant que l'adresse de base des périphériques APB1 est 0 x 40000000. , alors vous connaissez l'adresse de base de l'adresse USART3, quel est le décalage de USART3 ? Une méthode astucieuse est utilisée ici, c'est-à-dire que la variable de structure fait référence au décalage.
insérez la description de l'image ici
À ce stade, les variables de membre de structure de USART sont dans le même ordre que les registres réels dans l'adresse. Chaque membre de la structure correspond simplement à chaque registre réel. En fait, l'adresse du membre de structure spécifié est cohérente avec l'adresse du registre périphérique correspondant. Cela résout le problème de décalage. &USART3->DATAR est de spécifier le pointeur de structure de USART3, pointant vers le membre DATAR est d'ajouter l'adresse de décalage.

Voici un autre point de connaissance :
dans les systèmes embarqués, les types de données couramment utilisés incluent uint8_t, uint16_t, uint32_t, etc., car leur nombre de bits est faible, ce qui peut économiser de l'espace mémoire. Pendant la transmission des données, un type de données approprié peut être sélectionné pour la transmission en fonction des besoins réels.
uint8_t : représente un entier non signé de 8 bits, la plage de valeurs est comprise entre 0 et 255 ;
uint16_t : représente un entier non signé de 16 bits, la plage de valeurs est de 0 à 65 535 ;
uint32_t : représente un entier non signé de 32 bits, la plage de valeurs est 0~4294967295 .
De même, int8_t est un type entier 8 bits signé et sa plage de valeurs est -128~127.
Les fonctions de u8 et uint8_t sont les mêmes, les deux sont utilisées pour représenter des entiers 8 bits non signés. Cependant, u8 est généralement un type personnalisé dans certains scénarios spécifiques (comme un compilateur intégré, et uint8_t est un type intégré en langage C. C'est-à-dire, par exemple, lorsque les données dont nous disposons ne dépassent pas 255,
nous peut définir le tableau en tant que données uint8_t [100], ce qui peut considérablement économiser de l'espace mémoire.En même temps, par exemple, le port série reçoit et envoie des données en unités d'un octet, et la définition d'un entier de 8 bits est également propice à la fonction du port série.

Deuxièmement, le code spécifique

Ici, j'utilise deux cartes de développement, l'une est la carte de développement STM32F103RCT6 et l'autre est la carte de développement Qinheng CH32V307.Les fonctions à réaliser sont : STM32 en tant qu'expéditeur utilisant le port série DMA +, créez deux fonctions
pour générer des valeurs de température et d'humidité Il est stocké dans un tableau et les données du tableau sont envoyées à l'extrémité de réception au moyen d'un port série + DMA .
En tant que récepteur utilisant DMA + port série, CH32V307 distingue les données dans le tableau reçu et les imprime avec l'assistant de débogage du port série .

1. Configuration DMA STM32 (expéditeur)

(1) En tant qu'expéditeur, les données seront naturellement envoyées de la mémoire au registre de données du port série.Je définis un tableau et écris deux fonctions pour renvoyer les valeurs de température et d'humidité respectivement comme éléments du tableau.

uint8_t data[2];
uint8_t get_temperature(){
    
    
	data[0]=rand() % 126 ;
    return data[0];// 限定温度在-40到85摄氏度之间
}
uint8_t get_humi(){
    
    
    data[1]=rand() % 101; // 限定湿度在0到100%之间
	return data[1];
}

(2) Configurez DMA, la direction est de la mémoire aux registres périphériques

Le port série 2 est utilisé ici. Vérifiez la fiche technique et constatez que la fonction TX de USART2 correspond au canal 7 de DMA1, alors faites attention lors de la configuration.

insérez la description de l'image ici
Configurez d'abord le port série 2 normalement, le débit en bauds est de 9600.
code afficher comme ci-dessous:

void Init_USART2(){
    
    
  
	GPIO_InitTypeDef GPIO_InitStructure;//声明一个结构体对象
	USART_InitTypeDef  USART_InitStructure;
	//NVIC_InitTypeDef NVIC_InitStructure;
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//USART2挂载APB1总线
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOA挂载APB2总线
  //对于哪个应用挂载哪个APB总线,可以根据代码自动补全功能快捷判断

	//TX端口-PA2
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//这个对象的成员变量GPIO_Pin取值为pin2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//模式为复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHZ速度
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	//RX端口-PA3
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//这个对象的成员变量GPIO_Pin取值为pin3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//模式为浮空输入模式
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//
  USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//收发模式并存
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//八位数据位
	USART_Init(USART2,&USART_InitStructure);
	//USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);//开启串口2的中断接收
	USART_Cmd(USART2,ENABLE);
	
}

(3) Configurer l'initialisation DMA

void USART2_DMA_Tx_Configuration(void)
{
    
    
	DMA_InitTypeDef  DMA_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2 , ENABLE);						//DMA2时钟使能
	DMA_DeInit(DMA1_Channel7);
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;		//DMA外设地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART2_DMA_TX_Buffer;	//发送缓存指针
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;						//传输方向,从内存到外设
    DMA_InitStructure.DMA_BufferSize = USART2_DMA_TX_BUFFER_MAX_LENGTH;		//传输长度
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;				//内存递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度:BYTE
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//内存数据宽度:BYTE
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//循环模式:否//(注:DMA_Mode_Normal为正常模式,DMA_Mode_Circular为循环模式)
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 				//优先级:高
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 							//内存:内存(都)
	DMA_Init(DMA1_Channel7 , &DMA_InitStructure);							//初始化DMA1_Channel4
	//DMA_ClearFlag(DMA1_FLAG_GL4);
	DMA_ClearFlag(DMA1_FLAG_GL7);
	DMA_Cmd(DMA1_Channel7 , DISABLE); 										//禁用DMA通道传输
	USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);                          //开启串口DMA发送
}

(4) DMA active la fonction de transfert

void DMA_send(){
    
    
  //开启计数器,在传输过程中,DMA控制器会持续地递减该计数器的值,直到计数器为0,表示数据传输完成。
  int len = sizeof(data);
  memcpy(USART2_DMA_TX_Buffer, (uint8_t*)data, len);
  DMA_SetCurrDataCounter(DMA1_Channel7,USART2_DMA_TX_BUFFER_MAX_LENGTH);
  DMA_Cmd(DMA1_Channel7, ENABLE);//开启DMA传输
  while(DMA_GetFlagStatus(DMA1_FLAG_TC7) != SET);
 
  DMA_Cmd(DMA1_Channel7, DISABLE);//关闭DMA传输
  DMA_ClearFlag(DMA1_FLAG_TC7);

}

(5) Programme principal

 int main(void)
 {
    
    	
	 delay_init();	    //延时函数初始化	  
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
	 uart_init(9600);
	 Init_USART2();
	 USART2_DMA_Tx_Configuration();
	 printf("666");
	while(1)
	{
    
       get_humi();
		get_temperature();
		DMA_send();
		delay_ms(1000);
	}
 }

2. CH32V307 (récepteur) adopte la réception d'interruption de port série ordinaire

Tout d'abord, utilisez la méthode de réception du port série commun.

void USART2_IRQHandler(void)
{
    
    
    u8 Res;
    int i;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        {
    
    
      static uint8_t idx=0;//当前接收到的字节数
      static uint8_t* ptr=(uint8_t*)res_data;//将数据转化为字节数组  
      Res=USART_ReceiveData(USART2);//读取接收到的字节
      if(idx<data_length*sizeof(int)){
    
    //如果数据还未接受完
          ptr[idx++]=Res;//将接收到的数据存储到数组中
      }
      if (idx==data_length*sizeof(int)) {
    
    //如果数据接收完毕
          idx=0;
        for ( i = 0; i < data_length;i++) {
    
    
            res_data[i]=*((int*)(ptr+i*sizeof(int)));
        }
    }
      USART_ClearITPendingBit(USART2, USART_IT_RXNE); // 清除接收中断标志位
        }

}

Cette méthode est relativement courante et consomme des ressources CPU. Par exemple, si j'envoie 100 octets de données, le CPU entrera fréquemment 100 interruptions, ce qui n'est évidemment pas aussi bon que DMA, qui n'interrompt qu'une seule fois après que toutes les données sont empaquetées et envoyé.

3. Configuration DMA CH32V307 (récepteur)

Utilisez également le port série 2 de CH32V307 pour configurer la réception DMA.

(1) En tant que récepteur, les données naturelles doivent être envoyées du registre de données du port série vers la mémoire, de sorte que la configuration DMA doit être modifiée.

//DMA1的通道6对应USART2的RX
void DMA_RX_init(){
    
    
    DMA_InitTypeDef  DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);                     //DMA2时钟使能

    DMA_DeInit(DMA1_Channel6);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DATAR;       //DMA外设地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART2_RxBuf;    //发送缓存指针
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                        //传输方向,从外设到内存
    DMA_InitStructure.DMA_BufferSize = USART_MAX_LEN;       //传输长度
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;      //外设递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;               //内存递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;   //外设数据宽度:BYTE
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;           //内存数据宽度:BYTE
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                         //循环模式:否//(注:DMA_Mode_Normal为正常模式,DMA_Mode_Circular为循环模式)
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;               //优先级:高
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                          //内存:内存(都)
    DMA_Init(DMA1_Channel6 , &DMA_InitStructure);                           //初始化DMA1_Channel4
        //DMA_ClearFlag(DMA1_FLAG_GL4);
    //DMA_ClearFlag(DMA1_FLAG_GL6);
    DMA_Cmd(DMA1_Channel6 , DISABLE);                                       //禁用DMA通道传输
    USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);                          //开启串口DMA接收
    USART_Cmd(USART2, ENABLE);      //使能串口
}

(2) Programme de démarrage DMA

void USART2_Server(){
    
    

        uint16_t i,len;
       // len = USART_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel6);    // 获取接收到的数据长度 单位为字节
   //DMA_SetCurrDataCounter(DMA1_Channel6,USART_MAX_LEN); // 重新赋值计数值,必须大于等于最大可能接收到的数据帧数目
   DMA_Cmd(DMA1_Channel6 , ENABLE);
   DMA_SetCurrDataCounter(DMA1_Channel6,USART_MAX_LEN); // 重新赋值计数值,必须大于等于最大可能接收到的数据帧数目
        //USART_ReceiveData(USART2);                                      // 清除空闲中断标志位(接收函数有清标志位的作用)
     //printf("data=%d\r\n",USART_ReceiveData(USART2));
    // printf("data1=%d\r\n",USART2_RxBuf[0]);
        //DMA_Cmd(DMA1_Channel6, DISABLE);                                // 关闭DMA1_Channel6不再接收数据

        while(DMA_GetFlagStatus(DMA1_FLAG_TC6)==RESET);
        DMA_Cmd(DMA1_Channel6, DISABLE);
        DMA_ClearFlag(DMA1_FLAG_TC6);                                   // 清DMA1_Channel6接收完成标志位
        //DMA_SetCurrDataCounter(DMA1_Channel6,USART_MAX_LEN);            // 重新赋值计数值,必须大于等于最大可能接收到的数据帧数目
        //for (i = 0; i < len; ++i) {                                     // 把接收到的数据转移到发送数组
          //  res_data[i] = USART2_RxBuf[i];
         //   printf("res%d=%d\r\n",i,res_data[i]);
        }

(3) Programme principal

extern uint8_t USART2_RxBuf[USART_MAX_LEN];   //接收缓存
extern uint8_t res_data[2];
int main(void)
{
    
    
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	SystemCoreClockUpdate();
	Delay_Init();
	USART_Printf_Init(115200);
	printf("SystemClk:%d\r\n", SystemCoreClock);
	//printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
	printf("RTC Test\r\n");
   // USART3_INIT(9600);
    uart2_init(9600);
    DMA_RX_init();
    printf(" Test\r\n");
 	while(1)
    {
    
    
 	    USART2_Server();
 	    printf("res_data[0]=%d\r\n",USART2_RxBuf[0]);
 	    printf("res_data[1]=%d\r\n",USART2_RxBuf[1]);
 	    Delay_Ms(1000);
    }
}

3. Résultats:

insérez la description de l'image ici
Lorsque l'extrémité émettrice génère des données aléatoires, les données sont transférées de la mémoire au registre de données du port série 2 et envoyées à l'extrémité réceptrice. Le registre de données côté réception est transféré dans la mémoire désignée par DMA. Comme le montre la figure, les résultats expérimentaux sont corrects.

Résumer

DMA est vraiment utile. Mais je n'ai pas encore utilisé l'interruption DMA+, je pourrais essayer plus tard.

Je suppose que tu aimes

Origine blog.csdn.net/qq_53092944/article/details/130673554
conseillé
Classement