嵌入式开发笔记——调试组件SEGGER_HardFaultHandle
一、前言
在使用Cortex-M内核的MCU进行开发时,有时候会因为对内存错误访问等原因造成程序产生异常从而进入HardFaultHandler错误中断。如果程序结构比较复杂,尤其是运行了RTOS时可能短时间内不易定位异常产生的原因。Segger提供了一种分析CortexM内核芯片HardFault的方法,我在项目中使用后感觉该方法比较实用,本文用来记录该异常分析组件的使用。
二、组件添加
在SEGGER官网的Application Notes页面下提供了该组件的源码和文档

下载下来后将源文件添加到工程中,然后将中断处理文件中的void HardFault_Handler(void)函数屏蔽掉(和添加的文件中的函数有冲突)就完成添加了。

三、组件应用
执行以下代码使程序进入HardFault_Handler
volatile unsigned int* p;
p = (unsigned int*)0x007FFFFF;	// 小于0x8000000的地址在STM32中无效
*p = 0x123456;
SEGGER_RTT_printf(0, "p = %d\r\n", *p);
在调试模式下运行程序会停在SEGGER_HardFaultHandler.c文件中的void HardFaultHandler(unsigned int* pStack)函数中。
/*********************************************************************
*
*       HardFaultHandler()
*
*  Function description
*    C part of the hard fault handler which is called by the assembler
*    function HardFault_Handler
*/
void HardFaultHandler(unsigned int* pStack) {
	SEGGER_RTT_printf(0, "system enter hard-fault\r\n");
  //
  // In case we received a hard fault because of a breakpoint instruction, we return.
  // This may happen when using semihosting for printf outputs and no debugger is connected,
  // i.e. when running a "Debug" configuration in release mode.
  //
  if (NVIC_HFSR & (1u << 31)) {
    NVIC_HFSR |=  (1u << 31);     // Reset Hard Fault status
    *(pStack + 6u) += 2u;         // PC is located on stack at SP + 24 bytes. Increment PC by 2 to skip break instruction.
    return;                       // Return to interrupted application
  }
#if DEBUG
  //
  // Read NVIC registers
  //
  HardFaultRegs.syshndctrl.byte = SYSHND_CTRL;  // System Handler Control and State Register
  HardFaultRegs.mfsr.byte       = NVIC_MFSR;    // Memory Fault Status Register
  HardFaultRegs.bfsr.byte       = NVIC_BFSR;    // Bus Fault Status Register
  HardFaultRegs.bfar            = NVIC_BFAR;    // Bus Fault Manage Address Register
  HardFaultRegs.ufsr.byte       = NVIC_UFSR;    // Usage Fault Status Register
  HardFaultRegs.hfsr.byte       = NVIC_HFSR;    // Hard Fault Status Register
  HardFaultRegs.dfsr.byte       = NVIC_DFSR;    // Debug Fault Status Register
  HardFaultRegs.afsr            = NVIC_AFSR;    // Auxiliary Fault Status Register
  //
  // Halt execution
  // If NVIC registers indicate readable memory, change the variable value to != 0 to continue execution.
  //
  _Continue = 0u;
  while (_Continue == 0u) {
  }
  //
  // Read saved registers from the stack.
  //
  HardFaultRegs.SavedRegs.r0       = pStack[0];  // Register R0
  HardFaultRegs.SavedRegs.r1       = pStack[1];  // Register R1
  HardFaultRegs.SavedRegs.r2       = pStack[2];  // Register R2
  HardFaultRegs.SavedRegs.r3       = pStack[3];  // Register R3
  HardFaultRegs.SavedRegs.r12      = pStack[4];  // Register R12
  HardFaultRegs.SavedRegs.lr       = pStack[5];  // Link register LR
  HardFaultRegs.SavedRegs.pc       = pStack[6];  // Program counter PC
  HardFaultRegs.SavedRegs.psr.byte = pStack[7];  // Program status word PSR
  //
  // Halt execution
  // To step out of the HardFaultHandler, change the variable value to != 0.
  //
  _Continue = 0u;
  while (_Continue == 0u) {
  }
#else
  //
  // If this module is included in a release configuration, simply stay in the HardFault handler
  //
  (void)pStack;
  do {
  } while (1);
#endif
}
在HardFaultRegs结构体中包含了用来分析异常原因的寄存器
static struct {
  struct {
    volatile unsigned int r0;            // Register R0
    volatile unsigned int r1;            // Register R1
    volatile unsigned int r2;            // Register R2
    volatile unsigned int r3;            // Register R3
    volatile unsigned int r12;           // Register R12
    volatile unsigned int lr;            // Link register
    volatile unsigned int pc;            // Program counter
    union {
      volatile unsigned int byte;
      struct {
        unsigned int IPSR : 8;           // Interrupt Program Status register (IPSR)
        unsigned int EPSR : 19;          // Execution Program Status register (EPSR)
        unsigned int APSR : 5;           // Application Program Status register (APSR)
      } bits;
    } psr;                               // Program status register.
  } SavedRegs;
  union {
    volatile unsigned int byte;
    struct {
      unsigned int MEMFAULTACT    : 1;   // Read as 1 if memory management fault is active
      unsigned int BUSFAULTACT    : 1;   // Read as 1 if bus fault exception is active
      unsigned int UnusedBits1    : 1;
      unsigned int USGFAULTACT    : 1;   // Read as 1 if usage fault exception is active
      unsigned int UnusedBits2    : 3;
      unsigned int SVCALLACT      : 1;   // Read as 1 if SVC exception is active
      unsigned int MONITORACT     : 1;   // Read as 1 if debug monitor exception is active
      unsigned int UnusedBits3    : 1;
      unsigned int PENDSVACT      : 1;   // Read as 1 if PendSV exception is active
      unsigned int SYSTICKACT     : 1;   // Read as 1 if SYSTICK exception is active
      unsigned int USGFAULTPENDED : 1;   // Usage fault pended; usage fault started but was replaced by a higher-priority exception
      unsigned int MEMFAULTPENDED : 1;   // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception
      unsigned int BUSFAULTPENDED : 1;   // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception
      unsigned int SVCALLPENDED   : 1;   // SVC pended; SVC was started but was replaced by a higher-priority exception
      unsigned int MEMFAULTENA    : 1;   // Memory management fault handler enable
      unsigned int BUSFAULTENA    : 1;   // Bus fault handler enable
      unsigned int USGFAULTENA    : 1;   // Usage fault handler enable
    } bits;
  } syshndctrl;                          // System Handler Control and State Register (0xE000ED24)
  union {
    volatile unsigned char byte;
    struct {
      unsigned char IACCVIOL    : 1;     // Instruction access violation
      unsigned char DACCVIOL    : 1;     // Data access violation
      unsigned char UnusedBits  : 1;
      unsigned char MUNSTKERR   : 1;     // Unstacking error
      unsigned char MSTKERR     : 1;     // Stacking error
      unsigned char UnusedBits2 : 2;
      unsigned char MMARVALID   : 1;     // Indicates the MMAR is valid
    } bits;
  } mfsr;                                // Memory Management Fault Status Register (0xE000ED28)
  union {
    volatile unsigned int byte;
    struct {
      unsigned int IBUSERR    : 1;       // Instruction access violation
      unsigned int PRECISERR  : 1;       // Precise data access violation
      unsigned int IMPREISERR : 1;       // Imprecise data access violation
      unsigned int UNSTKERR   : 1;       // Unstacking error
      unsigned int STKERR     : 1;       // Stacking error
      unsigned int UnusedBits : 2;
      unsigned int BFARVALID  : 1;       // Indicates BFAR is valid
    } bits;
  } bfsr;                                // Bus Fault Status Register (0xE000ED29)
  volatile unsigned int bfar;            // Bus Fault Manage Address Register (0xE000ED38)
  union {
    volatile unsigned short byte;
    struct {
      unsigned short UNDEFINSTR : 1;     // Attempts to execute an undefined instruction
      unsigned short INVSTATE   : 1;     // Attempts to switch to an invalid state (e.g., ARM)
      unsigned short INVPC      : 1;     // Attempts to do an exception with a bad value in the EXC_RETURN number
      unsigned short NOCP       : 1;     // Attempts to execute a coprocessor instruction
      unsigned short UnusedBits : 4;
      unsigned short UNALIGNED  : 1;     // Indicates that an unaligned access fault has taken place
      unsigned short DIVBYZERO  : 1;     // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)
    } bits;
  } ufsr;                                // Usage Fault Status Register (0xE000ED2A)
  union {
    volatile unsigned int byte;
    struct {
      unsigned int UnusedBits  : 1;
      unsigned int VECTBL      : 1;      // Indicates hard fault is caused by failed vector fetch
      unsigned int UnusedBits2 : 28;
      unsigned int FORCED      : 1;      // Indicates hard fault is taken because of bus fault/memory management fault/usage fault
      unsigned int DEBUGEVT    : 1;      // Indicates hard fault is triggered by debug event
    } bits;
  } hfsr;                                // Hard Fault Status Register (0xE000ED2C)
  union {
    volatile unsigned int byte;
    struct {
      unsigned int HALTED   : 1;         // Halt requested in NVIC
      unsigned int BKPT     : 1;         // BKPT instruction executed
      unsigned int DWTTRAP  : 1;         // DWT match occurred
      unsigned int VCATCH   : 1;         // Vector fetch occurred
      unsigned int EXTERNAL : 1;         // EDBGRQ signal asserted
    } bits;
  } dfsr;                                // Debug Fault Status Register (0xE000ED30)
  volatile unsigned int afsr;            // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)
} HardFaultRegs;
将HardFaultRegs结构体添加到Watch窗口中,通过一步步向下执行程序可以看到结构体中各参数状态,每个寄存器及寄存器bit位表示什么含义在结构体定义中均有说明。

将_Continue变量也添加到Watch窗口,当执行到以下代码处时在Watch窗口中改变变量值就可以继续向下执行。
//
// Halt execution
// If NVIC registers indicate readable memory, change the variable value to != 0 to continue execution.
//
_Continue = 0u;
while (_Continue == 0u) {
}

通过对寄存器分析可得出产生异常的原因。
四、扩展
Cortex-M故障异常
Cortex-M processors implement different fault exceptions.
HardFault Exception
The HardFault is the default exception, raised on any error which is not associated with another (enabled) exception.
The HardFault has a fixed priority of -1, i.e. it has a higher priority than all other interrupts and exceptions except for NMI. Therefore a HardFault exception handler can always be entered when an error happens in application code, an interrupt, or another exception. The HardFault is exception number 3 in the vector table with IRQ number -13.
MemManage Exception
The MemManage exception is available with the use of a Memory Protection Unit (MPU) to raise an exception on memory access violations.
The MemManage is exception number 4 in the vector table, IRQ Number -12, and has a configurable priority.
BusFault Exception
The BusFault exception is raised on any memory access error. E.g. by illegal read, write, or vector catch.
The BusFault is exception number 5 in the vector table, IRQ number -11, and has configurable priority. BusFaults can explicitly be enabled in the system control block (SCB). When BusFault is not enabled, a HardFault is raised.
UsageFault Exception
The UsageFault exception is raised on execution errors. Unaligned access on load/store multiple instructions are always caught. Exceptions on other unaligned access, as well as division by zero can be additionally enabled in the SCB.
The UsageFault is exception number 6 in the vector table, IRQ number -10, and has configurable priority. When UsageFault is not enabled, a HardFault is raised instead.
故障状态寄存器
The Cortex-M System Control Block (SCB) contains some registers which enable configuration of exceptions and provide information about faults.
HardFault Status Register (HFSR)
The HFSR is in the SCB at address 0xE000ED2C. It is a 32-bit register.
Bitfields:
[31] DEBUGEVT - Reserved for use by debugger/debug probe. Always write 0.
[30] FORCED   - If 1, HardFault has been caused by escalation of another exception, because it is disabled or because of priority.
[1]  VECTTBL  - If 1, a BusFault occurred by reading the vector table for exception processing.
UsageFault Status Register (UFSR)
The UFSR is a 16-bit pseudo-register, part of the Configurable Fault Status Register (CFSR) at address 0xE000ED28. It can also be directly accessed with halfword access to 0xE000ED2A.
Bitfields:
[9] DIVBYZERO  - If 1, SDIV or UDIV instruction executed with divisor 0.
[8] UNALIGNED  - If 1, LDM, STM, LDRD, STRD on unaligned address executed, or single load or store executed when enabled to trap.
[3] NOCP       - If 1, access to unsupported (e.g. not available or not enabled) coprocessor.
[2] INVPC      - If 1, illegal or invalid EXC_RETURN value load to PC.
[1] INVSTATE   - If 1, execution in invalid state. E.g. Thumb bit not set in EPSR, or invalid IT state in EPSR.
[0] UNDEFINSTR - If 1, execution of undefined instruction.
BusFault Status Register (BFSR) and BusFault Address Register (BFAR)
The BFSR is a 8-bit pseudo-register in the CFSR. It can be directly accessed with byte access ad 0xE000ED29. The BFAR is a 32-bit register at 0xE000ED38.
Bitfields:
[7] BFARVALID   - If 1, the BFAR contains the address which caused the BusFault.
[5] LSPERR      - 1f 1, fault during floating-point lazy stack preservation.
[4] STKERR      - If 1, fault on stacking for exception entry.
[3] UNSTKERR    - If 1, fault on unstacking on exception return.
[2] IMPRECISERR - If 1, return address is not related to fault, e.g. fault caused before.
[1] PRECISERR   - If 1, return address instruction caused the fault.
[0] IBUSERR     - If 1, fault on instruction fetch.
MemManage Fault Status Register (MMFSR) and MemManage fault Address Register (MMFAR)
The MMFSR is a 8-bit pseudo-register in the CFSR. It can be directly accessed with byte access ad 0xE000ED28. The MMFAR is a 32-bit register at 0xE000ED34.
Bitfields:
[7] MMARVALID - If 1, the MMFAR contains the address which caused the MemManageFault.
[5] MLSPERR   - 1f 1, fault during floating-point lazy stack preservation.
[4] MSTKERR   - If 1, fault on stacking for exception entry.
[3] MUNSTKERR - If 1, fault on unstacking on exception return.
[1] DACCVIOL  - If 1, data access violation.
[0] IACCVIOL  - If 1, instruction access violation.
Stack Recovery
On exception entry, the exception handler can check which stack has been used when the fault happened. When bit EXC_RETURN[2] is set, MSP has been used, otherwise PSP has been used.
The stack can be used to recover the CPU register values.
CPU Register Recovery
On exception entry, some CPU registers are stored on the stack and can be read from there for error analysis. the following registers are recoverable:
 r0       = pStack[0];  // Register R0
 r1       = pStack[1];  // Register R1
 r2       = pStack[2];  // Register R2
 r3       = pStack[3];  // Register R3
 r12      = pStack[4];  // Register R12
 lr       = pStack[5];  // Link register LR
 pc       = pStack[6];  // Program counter PC
 psr.byte = pStack[7];  // Program status word PSR
故障分析示例
The following examples show how/why some faults can be caused, and how to analyze them. A project to test the faults is available here.
BusFault Examples
Illegal Memory Write
/*********************************************************************
*
*       _IllegalWrite()
*
*  Function description
*   Trigger a BusFault or HardFault by writing to a reserved address.
*
*  Additional Information
*    BusFault is raised some instructions after the write instruction.
*    Related registers on fault:
*      HFSR = 0x40000000
*        FORCED = 1           - BusFault escalated to HardFault (when BusFault is not activated)
*      BFSR = 0x00000004
*        IMPREISERR = 1       - Imprecise data access violation. Return address not related to fault
*        BFARVALID = 0        - BFAR not valid
*/
static int _IllegalWrite(void) {
  int r;
  volatile unsigned int* p;
  r = 0;
  p = (unsigned int*)0x00100000;       // 0x00100000-0x07FFFFFF is reserved on STM32F4
  //  F44F1380    mov.w r3, #0x00100000
  *p = 0x00BADA55;
  //  4A03        ldr r2, =0x00BADA55
  //  601A        str r2, [r3]         <- Illegal write is done here
  return r;
  //  9B00        ldr r3, [sp]
  //  4618        mov r0, r3
  //  B002        add sp, sp, #8       <- Fault might be raised here
  //  4770        bx lr
}
Illegal Memory Read
/*********************************************************************
*
*       _IllegalRead()
*
*  Function description
*   Trigger a BusFault or HardFault by reading from a reserved address.
*
*  Additional Information
*    BusFault is immediately triggered on the read instruction.
*    Related registers on fault:
*      HFSR = 0x40000000
*        FORCED = 1           - BusFault escalated to HardFault
*      BFSR = 0x00000082
*        PRECISERR = 1        - Precise data access violation
*        BFARVALID = 1        - BFAR is valid
*      BFAR = 0x00100000      - The address read from
*/
static int _IllegalRead(void) {
  int r;
  volatile unsigned int* p;
  p = (unsigned int*)0x00100000;        // 0x00100000-0x07FFFFFF is reserved on STM32F4
  //  F44F1380    mov.w r3, #0x00100000 <- The read address. Will be found in BFAR
  r = *p;
  //  681B        ldr r3, [r3]          <- Illegal read happens here and raises BusFault
  //  9300        str r3, [sp]
  return r;
}
Illegal Function Execution
/*********************************************************************
*
*       _IllegalFunc()
*
*  Function description
*   Trigger a BusFault or HardFault by executing at a reserved address.
*
*  Additional Information
*    BusFault is triggered on execution at the invalid address.
*    Related registers on fault:
*      HFSR = 0x40000000
*        FORCED = 1           - BusFault escalated to HardFault
*      BFSR = 0x00000001
*        IBUSERR = 1          - BusFault on instruction prefetch
*/
static int _IllegalFunc(void) {
  int r;
  int (*pF)(void);
  pF = (int(*)(void))0x00100001;         // 0x00100000-0x07FFFFFF is reserved on STM32F4
  //  F44F1380    mov.w r3, #0x00100001
  r = pF();
  //  4798        blx r3                 <- Branch to illegal address, causes fetch from 0x00100000 and fault exception
  return r;
}
UsageFault Examples
Undefined Instruction Execution
/*********************************************************************
*
*       _UndefInst()
*
*  Function description
*   Trigger a UsageFault or HardFault by executing an undefined instruction.
*
*  Additional Information
*    UsageFault is triggered on execution at the invalid address.
*    Related registers on hard fault:
*      HFSR = 0x40000000
*        FORCED = 1           - UsageFault escalated to HardFault
*      UFSR = 0x0001
*        UNDEFINSTR = 1       - Undefined instruction executed
*/
static int _UndefInst(void) {
  static const unsigned short _UDF[4] = {0xDEAD, 0xDEAD, 0xDEAD, 0xDEAD}; // 0xDEAD: UDF #<imm> (permanently undefined)
  int r;
  int (*pF)(void);
  pF = (int(*)(void))(((char*)&_UDF) + 1);
  //  4B05        ldr r3, =0x08001C18 <_UDF> <- Load address of "RAM Code" instructions
  //  3301        adds r3, #1                <- Make sure Thumb bit is set
  r = pF();
  //  4798        blx r3                     <- Call "RAM Code", will execute UDF instruction and raise exception
  //  9000        str r0, [sp]
  return r;
}
Illegal State
/*********************************************************************
*
*       _NoThumbFunc()
*
*  Function description
*   Trigger a UsageFault or HardFault by executing an address without thumb bit set.
*
*  Additional Information
*    UsageFault is triggered on execution at the invalid address.
*    Related registers on hard fault:
*      HFSR = 0x40000000
*        FORCED = 1           - UsageFault escalated to HardFault
*      UFSR = 0x0002
*        INVSTATE = 1         - Instruction execution with invalid state
*/
static int _NoThumbFunc(void) {
  int r;
  int (*pF)(void);
  pF = (int(*)(void))0x00100000;         // 0x00100000-0x07FFFFFF is reserved on STM32F4
  //  F44F1380    mov.w r3, #0x00100000  <- Note that bit [0] is not set.
  r = pF();
  //  4798        blx r3                 <- Branch exchange with mode change to ARM, but Cortex-M only supports Thumb mode.
  return r;
}
Division By Zero
/*********************************************************************
*
*       _DivideByZero()
*
*  Function description
*   Trigger a UsageFault or HardFault by dividing by zero.
*
*  Additional Information
*    UsageFault is triggered immediately on the divide instruction.
*    Related registers on hard fault:
*      HFSR = 0x40000000
*        FORCED = 1           - UsageFault escalated to HardFault
*      UFSR = 0x0200
*        DIVBYZERO = 1        - Divide-by-zero fault
*/
static int _DivideByZero(void) {
  int r;
  volatile unsigned int a;
  volatile unsigned int b;
  a = 1;
  //  2301        movs r3, #1       <- Load dividend
  b = 0;
  //  2300        movs r3, #0       <- Load divisor
  r = a / b;
  //  FBB2F3F3    udiv r3, r2, r3   <- divide by 0 raises fault exception
  return r;
}
Unaligned Access
/*********************************************************************
*
*       _UnalignedAccess()
*
*  Function description
*   Trigger a UsageFault or HardFault by an unaligned word access.
*
*  Additional Information
*    UsageFault is triggered immediately on the read or write instruction.
*    Related registers on fault:
*      HFSR = 0x40000000
*        FORCED = 1           - UsageFault escalated to HardFault
*      UFSR = 0x0100
*        UNALIGNED = 1        - Unaligned memory access
*/
static int _UnalignedAccess(void) {
  int r;
  volatile unsigned int* p;
  p = (unsigned int*)0x20000002;
  //  4B04        ldr r3, =0x20000002  <- Not word aligned address
  r = *p;
  //  681B        ldr r3, [r3]         <- Load word from unaligned address raises exception
  //  9300        str r3, [sp]
  return r;
}
HardFault Examples
Illegal Vector Table Fetch
/*********************************************************************
*
*       _IllegalVector()
*
*  Function description
*   Trigger a HardFault by interrupt with illegal vector table.
*
*  Additional Information
*    Related registers on fault:
*      HFSR = 0x00000002
*        VECTTBL = 1           - Vector table read fault
*/
static int _IllegalVector(void) {
  int r;
  SCB->VTOR = 0x001000000;            // Relocate vector table to illegal address
  //  4B09        ldr r3, =0xE000ED00
  //  F04F7280    mov.w r2, #0x1000000
  //  609A        str r2, [r3, #8]
  SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; // Trigger PendSV exception to read invalid vector
  //  4B07        ldr r3, =0xE000ED00
  //  F04F5280    mov.w r2, #0x10000000
  //  605A        str r2, [r3, #4]
  __ISB();
  //  F3BF8F6F    isb                 <- PendSV exception is to be executed. PendSV vector is tried to be read from illegal address 0x00100038 causes fault exception
  //  BF00        nop
  __DSB();
  //  F3BF8F4F    dsb sy
  //  BF00        nop
  return r;
}
												
											嵌入式开发笔记——调试组件SEGGER_HardFaultHandle的更多相关文章
- 嵌入式开发笔记——调试组件SEGGER_RTT
		
一.前言 在嵌入式开发过程中,经常会通过打印输出一些调试信息来调试参数.查找问题等,通常我的做法都是使用芯片的串口硬件设备配合串口助手软件来进行调试.但是这次项目的PCB硬件设计并未预留串口调试接口, ...
 - 安卓开发笔记——TabHost组件(二)(实现底部菜单导航)
		
上面文章<安卓开发复习笔记——TabHost组件(一)(实现底部菜单导航)>中提到了利用自定义View(ImageView+TextView)来设置一个底部菜单的样式 这边再补充一种更为灵 ...
 - 安卓开发笔记——TabHost组件(一)(实现底部菜单导航)
		
什么是TabHost? TabHost组件的主要功能是可以进行应用程序分类管理,例如:在用户使用windows操作系统的时候,经常见到如图所示的图形界面. TabHost选项卡,说到这个组件, ...
 - 安卓开发笔记——WebView组件
		
我们专业方向本是JAVA Web,这学期突然来了个手机App开发的课设,对于安卓这块,之前自学过一段时间,有些东西太久没用已经淡忘了 准备随笔记录些复习笔记,也当做温故知新吧~ 1.什么是WebVie ...
 - 安卓开发笔记——ViewPager组件(仿微信引导界面)
		
这2天事情比较多,都没时间更新博客,趁周末,继续继续~ 今天来讲个比较新潮的组件——ViewPager 什么是ViewPager? ViewPager是安卓3.0之后提供的新特性,继承自ViewGro ...
 - 安卓开发笔记——Gallery组件+ImageSwitcher组件
		
什么是Gallery? Gallery是一个水平的列表选择框,它允许用户通过拖动来查看上一个.下一个列表选项. 下图是今天要实现的最终效果: 利用Gallery组件实现的一个横向显示图像列表,可以通过 ...
 - 安卓开发笔记——GridView组件
		
1.什么是GridView? GridView(网格视图)是按照行列的方式来显示内容的,一般用于显示图片,图片等内容,比如实现九宫格图,用GridView是首选,也是最简单的. 2.正文 GridVi ...
 - 嵌入式开发笔记 - U-Boot相关
		
1.U-boot使用准备 1.1 U-boot下载 通过德国的denx软件中心提供的FTP下载合集,下载网址: ftp://ftp.denx.de/pub/u-boot/
 - Android 开发笔记 “广播组件使用”
		
在Activity中,注册广播的一个Demo. 总共分3步 第一步:定义一个BroadcastReceiver广播接收类: private BroadcastReceiver mBroadcastRe ...
 
随机推荐
- MindManager中主题间距/线条粗细的灵活调整
			
在MindManager中,主题和线条是思维导图的基本元素,只有通过它们才能将要表达的思想呈现.并联系起来.因此,关于它们的属性设置就会多一点,如颜色.宽度.位置等.而调整主题之间的距离及线条的粗细, ...
 - zabbix 监控域名证书到期时间!!!!
			
在客户端机器上创建脚本 vim /etc/zabbix/zabbix_agentd.d/check-cert-expire.sh #!/bin/sh host=$1port=$2end_date=`o ...
 - 蓝桥杯-RP大冒险-未解决
			
RP大冒险 问题描述 请尽情使用各种各样的函数来测试你的RP吧~~~ 输入格式 一个数N表示测点编号. 输出格式 一个0~9的数. 样例输入 0 样例输出 X {当且仅当输出仅有一个数X且X为0~9的 ...
 - 基础知识redis详解--【Foam番茄】
			
Redis 学习方式: 上手就用 基本的理论先学习,然后将知识融汇贯通 nosql讲解 为什么要用Nosql 现在都是大数据时代 大数据一般的数据库无法进行分析处理了 至少要会Springboot+S ...
 - nginx + lua-nginx-module 编译
			
摘要:本文主要介绍如何将lua-nginx-module 编译到nginx主程序中. nginx是一个高性能的反向代理服务器,lua是一个小巧的脚本语言,利用lua-nginx-module模块可以使 ...
 - DevOps Workshop | 代码管理入门:基于代码扫描实现团队效率提升
			
CODING「DevOps Workshop 学习营地」持续火热进行中! 在这里,你可以轻松实践 DevOps 全流程.体验高效的云端开发.赢取精美礼品--第二期大奖「戴尔 U2718Q 显示器」将于 ...
 - 通过自定义拦截器优雅的导出Excel并标红的重复数据
			
平时我们导入导出Excel的时候如果用poi导出,会发现光设置格式都要很多代码,看起来非常的不优雅.后来业务中遇到了需要导入非常巨大的Excel的需求.如果继续用poi的方式,因为poi把所有exce ...
 - bootstrap 按钮颜色属性
			
bootstrap 按钮颜色属性有几种
 - 学习笔记(1):零基础掌握 Python 入门到实战-列表与元祖到底该用哪个?(二)...
			
立即学习:https://edu.csdn.net/course/play/26676/338778?utm_source=blogtoedu 列表不能通过增加索引增加元素 可以使用list中的app ...
 - redis集群管理--sentinel
			
什么是sentinel? Sentinel(哨兵)是用于监控redis集群中Master状态的工具,是Redis 的高可用性解决方案,sentinel哨兵模式已经被集成在redis2.4之后的版本中. ...