【Cortex-M3 CMSIS内核驱动文件详解】4:内核功能接口

文章目录

  • 四、内核功能接口
    • 4.1 NVIC功能接口
      • 4.1.1 配置优先级分组
      • 4.1.2 获取优先级分组
      • 4.1.3 关闭使能相应的中断(IRQ,非系统错误)
      • 4.1.4 获取中断挂起(IRQ,非系统错误)
      • 4.1.5 设置外部中断挂起(IRQ,非系统错误)
      • 4.1.6 清除外部中断挂起(IRQ,非系统错误)
      • 4.1.7 获取外部中断的活动位(IRQ,非系统错误)
      • 4.1.8 设置中断优先级(系统异常+外部中断)
      • 4.1.9 获取中断优先级(系统异常+外部中断)
      • 4.1.10 对中断优先级编码
      • 4.1.11 对中断优先级解码
      • 4.1.12 使能相应的中断(IRQ,非系统错误)
    • 4.2 SysTick功能接口
      • 4.2.1 初始化并启动SysTick计数器和它的中断
    • 4.3 Reset功能接口
      • 4.3.1 发起系统复位请求
    • 4.4 调试输入输出功能接口
      • 4.4.1 输出字符在ITM通道0
      • 4.4.2 用ITM_RxBuffer接收字符
      • 4.4.3 通过变量 ITM_RxBuffer 检查字符是否可用

四、内核功能接口

内核功能接口主要包括:

  • NVIC接口;
  • SysTick接口;
  • Reset接口。

4.1 NVIC功能接口

4.1.1 配置优先级分组

/*** @brief  Set the Priority Grouping in NVIC Interrupt Controller** @param  PriorityGroup is priority grouping field** Set the priority grouping field using the required unlock sequence.* The parameter priority_grouping is assigned to the field * SCB->AIRCR [10:8] PRIGROUP field. Only values from 0..7 are used.* In case of a conflict between priority grouping and available* priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set.*/
static __INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{uint32_t reg_value;uint32_t PriorityGroupTmp = (PriorityGroup & 0x07);                         /* only values 0..7 are used          */reg_value  =  SCB->AIRCR;                                                   /* read old register configuration    */reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk);             /* clear bits to change               */reg_value  =  (reg_value                       |(0x5FA << SCB_AIRCR_VECTKEY_Pos) | (PriorityGroupTmp << 8));                                     /* Insert write key and priorty group */SCB->AIRCR =  reg_value;
}

image.png

注意点

  1. 配置优先级分组,需要配置SCB_AIRCR寄存器。
  2. SCB_AIRCR寄存器提供了处理模式需要的优先级分组、数据访问的端状态和系统的复位控制。
  3. SCB_AIRCR寄存器需要特权等级访问。
  4. 写SCB_AIRCR寄存器前需要写0x5FA给VECTKEY,否则处理器写无效。
  5. 注意优先级有效位__NVIC_PRIO_BITS

接口功能解析

  1. 保留输入有效值;
  2. 用定义的掩码清零VECTKEYPRIGROUP区域;
  3. 打开写保护的同时配置优先级分组。

4.1.2 获取优先级分组

/*** @brief  Get the Priority Grouping from NVIC Interrupt Controller** @return priority grouping field ** Get the priority grouping from NVIC Interrupt Controller.* priority grouping is SCB->AIRCR [10:8] PRIGROUP field.*/
static __INLINE uint32_t NVIC_GetPriorityGrouping(void)
{return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos);   /* read priority grouping field */
}

接口功能解析

  1. 移位返回。

4.1.3 关闭使能相应的中断(IRQ,非系统错误)

/*** @brief  Get the Priority Grouping from NVIC Interrupt Controller** @return priority grouping field ** Get the priority grouping from NVIC Interrupt Controller.* priority grouping is SCB->AIRCR [10:8] PRIGROUP field.*/
static __INLINE uint32_t NVIC_GetPriorityGrouping(void)
{return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos);   /* read priority grouping field */
}

注意

  1. IRQn >= 16。
  2. NVIC_ICERx不包括0~15系统错误。

4.1.4 获取中断挂起(IRQ,非系统错误)

/*** @brief  Read the interrupt pending bit for a device specific interrupt source* * @param  IRQn    The number of the device specifc interrupt* @return         1 = interrupt pending, 0 = interrupt not pending** Read the pending register in NVIC and return 1 if its status is pending, * otherwise it returns 0*/
static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)
{return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */
}

接口功能解析

  • 使用三目运算符判断挂起寄存器。

4.1.5 设置外部中断挂起(IRQ,非系统错误)

/*** @brief  Set the pending bit for an external interrupt* * @param  IRQn    The number of the interrupt for set pending** Set the pending bit for the specified interrupt.* The interrupt number cannot be a negative value.*/
static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn)
{NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */
}

4.1.6 清除外部中断挂起(IRQ,非系统错误)

/*** @brief  Clear the pending bit for an external interrupt** @param  IRQn    The number of the interrupt for clear pending** Clear the pending bit for the specified interrupt. * The interrupt number cannot be a negative value.*/
static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */
}

4.1.7 获取外部中断的活动位(IRQ,非系统错误)

/*** @brief  Read the active bit for an external interrupt** @param  IRQn    The number of the interrupt for read active bit* @return         1 = interrupt active, 0 = interrupt not active** Read the active register in NVIC and returns 1 if its status is active, * otherwise it returns 0.*/
static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
{return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */
}

4.1.8 设置中断优先级(系统异常+外部中断)

/*** @brief  Set the priority for an interrupt** @param  IRQn      The number of the interrupt for set priority* @param  priority  The priority to set** Set the priority for the specified interrupt. The interrupt * number can be positive to specify an external (device specific) * interrupt, or negative to specify an internal (core) interrupt.** Note: The priority cannot be set for every core interrupt.*/
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{if(IRQn < 0) {SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */else {NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
}

注意

  • 用正负判断系统异常和外部中断。

4.1.9 获取中断优先级(系统异常+外部中断)

/*** @brief  Read the priority for an interrupt** @param  IRQn      The number of the interrupt for get priority* @return           The priority for the interrupt** Read the priority for the specified interrupt. The interrupt * number can be positive to specify an external (device specific) * interrupt, or negative to specify an internal (core) interrupt.** The returned priority value is automatically aligned to the implemented* priority bits of the microcontroller.** Note: The priority cannot be set for every core interrupt.*/
static __INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn)
{if(IRQn < 0) {return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS)));  } /* get priority for Cortex-M3 system interrupts */else {return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)]           >> (8 - __NVIC_PRIO_BITS)));  } /* get priority for device specific interrupts  */
}

4.1.10 对中断优先级编码

/*** @brief  Encode the priority for an interrupt** @param  PriorityGroup    The used priority group* @param  PreemptPriority  The preemptive priority value (starting from 0)* @param  SubPriority      The sub priority value (starting from 0)* @return                  The encoded priority for the interrupt** Encode the priority for an interrupt with the given priority group,* preemptive priority value and sub priority value.* In case of a conflict between priority grouping and available* priority bits (__NVIC_PRIO_BITS) the samllest possible priority group is set.** The returned priority value can be used for NVIC_SetPriority(...) function*/
static __INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
{uint32_t PriorityGroupTmp = (PriorityGroup & 0x07);          /* only values 0..7 are used          */uint32_t PreemptPriorityBits;uint32_t SubPriorityBits;PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp;SubPriorityBits     = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS;return (((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) |((SubPriority     & ((1 << (SubPriorityBits    )) - 1))));
}

4.1.11 对中断优先级解码

/*** @brief  Decode the priority of an interrupt** @param  Priority           The priority for the interrupt* @param  PriorityGroup      The used priority group* @param  pPreemptPriority   The preemptive priority value (starting from 0)* @param  pSubPriority       The sub priority value (starting from 0)** Decode an interrupt priority value with the given priority group to * preemptive priority value and sub priority value.* In case of a conflict between priority grouping and available* priority bits (__NVIC_PRIO_BITS) the samllest possible priority group is set.** The priority value can be retrieved with NVIC_GetPriority(...) function*/
static __INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority)
{uint32_t PriorityGroupTmp = (PriorityGroup & 0x07);          /* only values 0..7 are used          */uint32_t PreemptPriorityBits;uint32_t SubPriorityBits;PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp;SubPriorityBits     = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS;*pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1);*pSubPriority     = (Priority                   ) & ((1 << (SubPriorityBits    )) - 1);
}

4.1.12 使能相应的中断(IRQ,非系统错误)

/*** @brief  Enable Interrupt in NVIC Interrupt Controller** @param  IRQn   The positive number of the external interrupt to enable** Enable a device specific interupt in the NVIC interrupt controller.* The interrupt number cannot be a negative value.*/
static __INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* enable interrupt */
}

4.2 SysTick功能接口

4.2.1 初始化并启动SysTick计数器和它的中断

/*** @brief  Initialize and start the SysTick counter and its interrupt.** @param   ticks   number of ticks between two interrupts* @return  1 = failed, 0 = successful** Initialise the system tick timer and its interrupt and start the* system tick timer / counter in free running mode to generate * periodical interrupts.*/
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk   | SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */return (0);                                                  /* Function successful */
}

接口功能解析

  1. 参数检查;
  2. 配置重载寄存器;
  3. 配置SysTick中断优先级;
  4. 计数器现值清零;
  5. 配置时钟源为处理器时钟AHB;
  6. 使能时钟中断;
  7. 启动时钟;

4.3 Reset功能接口

4.3.1 发起系统复位请求

/*** @brief  Initiate a system reset request.** Initiate a system reset request to reset the MCU*/
static __INLINE void NVIC_SystemReset(void)
{SCB->AIRCR  = ((0x5FA << SCB_AIRCR_VECTKEY_Pos)      | (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | SCB_AIRCR_SYSRESETREQ_Msk);                   /* Keep priority group unchanged */__DSB();                                                     /* Ensure completion of memory access */              while(1);                                                    /* wait until reset */
}

注意

  • 复位时需要保持优先级分组不变。
  • 系统复位不会复位调试系统。

接口功能解析

  1. 配置写访问;
  2. 保持优先级分组不变;
  3. 发起系统复位信号;
  4. 等待内存访问完成;
  5. 等待系统复位。

4.4 调试输入输出功能接口

内核调试接口包括:

  1. 内核调试输入/输出功能;
  2. 内核调试定义;
  3. 内核调试变量。
extern volatile int ITM_RxBuffer;                    /*!< variable to receive characters                             */
#define             ITM_RXBUFFER_EMPTY    0x5AA55AA5 /*!< value identifying ITM_RxBuffer is ready for next character */

4.4.1 输出字符在ITM通道0

/*** @brief  Outputs a character via the ITM channel 0** @param  ch   character to output* @return      character to output** The function outputs a character via the ITM channel 0. * The function returns when no debugger is connected that has booked the output.  * It is blocking when a debugger is connected, but the previous character send is not transmitted. */
static __INLINE uint32_t ITM_SendChar (uint32_t ch)
{if ((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)  &&      /* Trace enabled */(ITM->TCR & ITM_TCR_ITMENA_Msk)                  &&      /* ITM enabled */(ITM->TER & (1ul << 0)        )                    )     /* ITM Port #0 enabled */{while (ITM->PORT[0].u32 == 0);ITM->PORT[0].u8 = (uint8_t) ch;}  return (ch);
}

4.4.2 用ITM_RxBuffer接收字符

/*** @brief  Inputs a character via variable ITM_RxBuffer** @return      received character, -1 = no character received** The function inputs a character via variable ITM_RxBuffer. * The function returns when no debugger is connected that has booked the output.  * It is blocking when a debugger is connected, but the previous character send is not transmitted. */
static __INLINE int ITM_ReceiveChar (void) {int ch = -1;                               /* no character available */if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) {ch = ITM_RxBuffer;ITM_RxBuffer = ITM_RXBUFFER_EMPTY;       /* ready for next character */}return (ch); 
}

4.4.3 通过变量 ITM_RxBuffer 检查字符是否可用

/*** @brief  Check if a character via variable ITM_RxBuffer is available** @return      1 = character available, 0 = no character available** The function checks  variable ITM_RxBuffer whether a character is available or not. * The function returns '1' if a character is available and '0' if no character is available. */
static __INLINE int ITM_CheckChar (void) {if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) {return (0);                                 /* no character available */} else {return (1);                                 /*    character available */}
}

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

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

相关文章

一个注解完美实现分布式锁(AOP)

前言 学习过Spring的小伙伴都知道AOP的强大&#xff0c;本文将通过Redisson结合AOP&#xff0c;仅需一个注解就能实现分布式锁。 &#x1f36d; 不会使用aop和redisson的小伙伴可以参考&#xff1a; 【学习总结】使Aop实现自定义日志注解-CSDN博客 【学习总结】使用分布式锁和…

CSS 鼠标经过放大元素 不影响其他元素

效果 .item:hover{transform: scale(1.1); /* 鼠标悬停时将元素放大 1.1 倍 */ }.item{transition: transform 0.3s ease; /* 添加过渡效果&#xff0c;使过渡更加平滑 */ }

【研发日记】Matlab/Simulink避坑指南(十一)——Delay周期Bug

文章目录 前言 背景介绍 问题描述 分析排查 解决方案 总结归纳 前言 见《研发日记&#xff0c;Matlab/Simulink避坑指南(六)——字节分割Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑指南(七)——数据溢出钳位Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑指…

MMDetection目标检测框架推理与参数量计算

模型推理 在使用MMDetection框架完成训练后便可以使用训练所得的权重文件进行推理了&#xff0c;具体可以使用MMDetection文件下的demo文件夹的image_demo.py文件。 from argparse import ArgumentParser from mmengine.logging import print_log from mmdet.apis import Det…

CSS-IN-JS Emotion

为什么会有css-in-js 优点 缺点 使用emotion插件库 npm i emotion/core emotion/styled使用时需要解析css属性 使用方式一&#xff1a; 通过注释告诉babel不讲jsx转化为react.create Element的调用&#xff0c;而是转化为jsx语法。会导致一个警告react未使用。 使用方式二&am…

Redis__三大日志

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724 &#x1f389; 主题&#xff1a;Redis__三大日志 ⏱️ 创作时间&#xff1a;2024年04月30日 ———————————————— 对于MySQL来说, 有…

ShellScript脚本编程(一)

什么是Shell Shell 是一个用 C 语言编写的程序&#xff0c;它是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言 Shell 是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问操作系统内核的服务 为什么…

Themis新篇章:老牌衍生品协议登陆Blast L2,探索全新经济模型

本文将深入分析 Themis 的最新经济模型&#xff0c;探讨其核心概念和机制、优势与创新之处、风险与挑战。 一、引言 随着区块链技术的不断发展&#xff0c;DeFi 衍生品项目逐渐成为市场的焦点。而用户体验的革新&#xff0c;进一步的金融创新&#xff0c;去中心化治理方案的优…

SpringCloud整合Gateway结合Nacos

目录 一、引入依赖 二、开启两个测试项目 2.1 order service ​编辑 2.2 user service 三、gateway项目 3.1 新建一个bootstrap.yml文件 3.2 将我们的的网关配置写道nacos里的配置里 3.3 测试&#xff1a;看能够根据网关路由到两个测试的项目 四、 优化 4.1 将项目打包…

字节跳动发起AI战争 寻找下一个TikTok

现如今在字节跳动&#xff0c;已近乎隐退的张一鸣&#xff0c;只重点关注两件事&#xff1a;其一&#xff0c;是风暴中的TikTok&#xff1b;其二&#xff0c;就是字节跳动正在全力追赶的AI战略业务。 提及字节的AI战略远望,多个接近字节的人士均认为,以Flow部门出品最为“正统…

pthread线程相关

LWP :轻量级 进程&#xff0c;本质仍是进程 进程 &#xff1a;独立地址空间&#xff0c;拥有PCB 线程&#xff1a;有独立的TCB&#xff0c;但没有独立的地址空间&#xff08;共享&#xff09; 区别 &#xff1a;在与是否共享地址文件 进程 &#xff08;独居&#xff09;&am…

数据结构:最小生成树(Prim算法和Kruskal算法)、图的最短路径(Dijkstra算法和Bellman-Ford算法)

什么是最小生成树&#xff1f;Prim算法和Kruskal算法是如何找到最小生成树的&#xff1f; 最小生成树是指在一个连通图中&#xff0c;通过连接所有节点并使得总权重最小的子图。 Prim算法和Kruskal算法是两种常用的算法&#xff0c;用于寻找最小生成树。 Prim算法的步骤如下&…

文件API及其操作

这里介绍两类文件操作、三个文件类。包括文件系统操作&#xff08;File类&#xff09;、文件内容操作&#xff08;操作字节流、操作字符流&#xff09; 1.文件类File 1.1.认识File类 &#xff08;1&#xff09;什么是File类呢&#xff1f;其实就是可以操作文件的一个类。通过…

C# dateTimePicker控件存取数据库问题

存入数据库时&#xff0c;先设置&#xff0c; dateTimePicker1.Format DateTimePickerFormat.Custom; dateTimePicker1.CustomFormat "yyyy-MM-dd HH:mm:ss"; 然后&#xff0c;dateTimePicker1.Text 就和textBox1.Text一样方式存入数据库&#xff1b;…

【Java EE】Mybatis之XML详解

文章目录 &#x1f38d;配置数据库连接和MyBatis&#x1f340;写持久层代码&#x1f338;添加mapper接口&#x1f338;添加UserInfoXMLMapper.xml&#x1f338;单元测试 &#x1f332;CRUD&#x1f338;增(Insert)&#x1f338;删(Delete)&#x1f338;改(Update)&#x1f338;…

低空经济+飞行汽车:eVTOL技术详解

低空经济是以各种有人驾驶和无人驾驶航空器的各类低空飞行活动为牵引&#xff0c;辐射带动相关领域融合发展的综合性经济形态。它广泛体现于第一、第二、第三产业之中&#xff0c;在促进经济发展、加强社会保障、服务国防事业等方面发挥着日益重要的作用。 飞行汽车&#xff0c…

Linux服务器常用命令总结

view查找日志关键词 注意日志级别&#xff0c;回车后等一会儿&#xff0c;因为文件可能比较大加载完需要时间 当内容显示出来后&#xff0c;使用“/关键词”搜索 回车就能搜到&#xff0c;n表示查找下一个&#xff0c;N表示查找上一个 find 查找 find Family -name book …

js APIS part2

什么是事件&#xff1f; 事件是在编程时系统内发生的 动作 或者发生的事情。比如用户在网页上 单击 一个按钮 什么是事件监听&#xff1f; 就是让程序检测是否有事件产生&#xff0c;一旦有事件触发&#xff0c;就立即调用一个函数做出响应&#xff0c;也称为 绑定事件或者注册…

Python爬取豆瓣电影Top250数据

任务 爬取豆瓣电影top250中的影片名称、影片海报、年份、地区、类型、评分、评价人数、总体评价&#xff0c;并输出到douban_top250.xlsx文件中 环境 Python 3.8 requests bs4 openpyxl 源码 # 创建一个新的Excel工作簿 workbook openpyxl.Workbook() # 获取默认的工作表…

酒水门店私域流量运营搭建执行规划方案

【干货资料持续更新&#xff0c;以防走丢】 酒水门店私域流量运营搭建执行规划方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 PPT可编辑&#xff08;完整资料包含以下内容&#xff09; 目录 精酿啤酒品牌私域执行运营的内容策划&#xff0c;涉及以下几个…